1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-05 09:18:06 +03:00
samba-mirror/source3/rpc_server/mdssvc/mdssvc.c
Ralph Boehme 3afa27a01c mdssvc: better support for search with mdfind from Macs
When searching Samba via Spotlight from a Mac with mdfind, only 50 results are
returned.

It seems the changes for bug #15342 where one step in the right
direction. There, a status indicator meaning "search is still being processed"
was implemented, returning a special status indicator (0x23) in a response, when
the query was still running in the backend, eg Elasticsearch, and we haven't got
any result when when the Mac already comes along asking for results of a query.

Turns out, we should also return 0x23, ie "search is still being processed" when
we have some initial search results from the backend. Otherwise mdfind will stop
querying for more results. It works in Finder, as the Finder by default employs
a "live" search where it just keeps polling for more results even after the
server returned an empty result set. The Finder just keeps on querying in some
interval, typically 4 seconds, and a Mac server Spotlight server might return
new results if new files where created that match the query, hence "live"
search.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=15463

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Noel Power <noel.power@suse.com>

Autobuild-User(master): Ralph Böhme <slow@samba.org>
Autobuild-Date(master): Wed Aug 30 10:51:40 UTC 2023 on atb-devel-224
2023-08-30 10:51:40 +00:00

1894 lines
42 KiB
C

/*
Unix SMB/CIFS implementation.
Main metadata server / Spotlight routines
Copyright (C) Ralph Boehme 2012-2014
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
#include "smbd/proto.h"
#include "librpc/gen_ndr/auth.h"
#include "dbwrap/dbwrap.h"
#include "lib/util/dlinklist.h"
#include "lib/util/util_tdb.h"
#include "lib/util/time_basic.h"
#include "lib/dbwrap/dbwrap_rbt.h"
#include "libcli/security/dom_sid.h"
#include "libcli/security/security.h"
#include "mdssvc.h"
#include "mdssvc_noindex.h"
#ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
#include "mdssvc_tracker.h"
#endif
#ifdef HAVE_SPOTLIGHT_BACKEND_ES
#include "mdssvc_es.h"
#endif
#include "lib/global_contexts.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_RPC_SRV
struct slrpc_cmd {
const char *name;
bool (*function)(struct mds_ctx *mds_ctx,
const DALLOC_CTX *query,
DALLOC_CTX *reply);
};
struct slq_destroy_state {
struct tevent_context *ev;
struct sl_query *slq;
};
/*
* This is a static global because we may be called multiple times and
* we only want one mdssvc_ctx per connection to Tracker.
*
* The client will bind multiple times to the mdssvc RPC service, once
* for every tree connect.
*/
static struct mdssvc_ctx *mdssvc_ctx = NULL;
/*
* If these functions return an error, they hit something like a non
* recoverable talloc error. Most errors are dealt with by returning
* an error code in the Spotlight RPC reply.
*/
static bool slrpc_fetch_properties(struct mds_ctx *mds_ctx,
const DALLOC_CTX *query, DALLOC_CTX *reply);
static bool slrpc_open_query(struct mds_ctx *mds_ctx,
const DALLOC_CTX *query, DALLOC_CTX *reply);
static bool slrpc_fetch_query_results(struct mds_ctx *mds_ctx,
const DALLOC_CTX *query, DALLOC_CTX *reply);
static bool slrpc_store_attributes(struct mds_ctx *mds_ctx,
const DALLOC_CTX *query, DALLOC_CTX *reply);
static bool slrpc_fetch_attributenames(struct mds_ctx *mds_ctx,
const DALLOC_CTX *query, DALLOC_CTX *reply);
static bool slrpc_fetch_attributes(struct mds_ctx *mds_ctx,
const DALLOC_CTX *query, DALLOC_CTX *reply);
static bool slrpc_close_query(struct mds_ctx *mds_ctx,
const DALLOC_CTX *query, DALLOC_CTX *reply);
/************************************************
* Misc utility functions
************************************************/
/**
* Add requested metadata for a query result element
*
* This could be rewritten to something more sophisticated like
* querying metadata from Tracker.
*
* If path or sp is NULL, simply add nil values for all attributes.
**/
static bool add_filemeta(struct mds_ctx *mds_ctx,
sl_array_t *reqinfo,
sl_array_t *fm_array,
const char *path,
const struct stat_ex *sp)
{
sl_array_t *meta;
sl_nil_t nil;
int i, metacount, result;
uint64_t uint64var;
sl_time_t sl_time;
char *p;
const char *attribute;
size_t nfc_len;
const char *nfc_path = path;
size_t nfd_buf_size;
char *nfd_path = NULL;
char *dest = NULL;
size_t dest_remaining;
size_t nconv;
metacount = dalloc_size(reqinfo);
if (metacount == 0 || path == NULL || sp == NULL) {
result = dalloc_add_copy(fm_array, &nil, sl_nil_t);
if (result != 0) {
return false;
}
return true;
}
meta = dalloc_zero(fm_array, sl_array_t);
if (meta == NULL) {
return false;
}
nfc_len = strlen(nfc_path);
/*
* Simple heuristic, strlen by two should give enough room for NFC to
* NFD conversion.
*/
nfd_buf_size = nfc_len * 2;
nfd_path = talloc_array(meta, char, nfd_buf_size);
if (nfd_path == NULL) {
return false;
}
dest = nfd_path;
dest_remaining = talloc_array_length(dest);
nconv = smb_iconv(mds_ctx->ic_nfc_to_nfd,
&nfc_path,
&nfc_len,
&dest,
&dest_remaining);
if (nconv == (size_t)-1) {
return false;
}
for (i = 0; i < metacount; i++) {
attribute = dalloc_get_object(reqinfo, i);
if (attribute == NULL) {
return false;
}
if (strcmp(attribute, "kMDItemDisplayName") == 0
|| strcmp(attribute, "kMDItemFSName") == 0) {
p = strrchr(nfd_path, '/');
if (p) {
result = dalloc_stradd(meta, p + 1);
if (result != 0) {
return false;
}
}
} else if (strcmp(attribute, "kMDItemPath") == 0) {
result = dalloc_stradd(meta, nfd_path);
if (result != 0) {
return false;
}
} else if (strcmp(attribute, "kMDItemFSSize") == 0) {
uint64var = sp->st_ex_size;
result = dalloc_add_copy(meta, &uint64var, uint64_t);
if (result != 0) {
return false;
}
} else if (strcmp(attribute, "kMDItemFSOwnerUserID") == 0) {
uint64var = sp->st_ex_uid;
result = dalloc_add_copy(meta, &uint64var, uint64_t);
if (result != 0) {
return false;
}
} else if (strcmp(attribute, "kMDItemFSOwnerGroupID") == 0) {
uint64var = sp->st_ex_gid;
result = dalloc_add_copy(meta, &uint64var, uint64_t);
if (result != 0) {
return false;
}
} else if (strcmp(attribute, "kMDItemFSContentChangeDate") == 0 ||
strcmp(attribute, "kMDItemContentModificationDate") == 0)
{
sl_time = convert_timespec_to_timeval(sp->st_ex_mtime);
result = dalloc_add_copy(meta, &sl_time, sl_time_t);
if (result != 0) {
return false;
}
} else {
result = dalloc_add_copy(meta, &nil, sl_nil_t);
if (result != 0) {
return false;
}
}
}
result = dalloc_add(fm_array, meta, sl_array_t);
if (result != 0) {
return false;
}
return true;
}
static int cnid_comp_fn(const void *p1, const void *p2)
{
const uint64_t *cnid1 = p1, *cnid2 = p2;
if (*cnid1 == *cnid2) {
return 0;
}
if (*cnid1 < *cnid2) {
return -1;
}
return 1;
}
/**
* Create a sorted copy of a CNID array
**/
static bool sort_cnids(struct sl_query *slq, const DALLOC_CTX *d)
{
uint64_t *cnids = NULL;
int i;
const void *p;
cnids = talloc_array(slq, uint64_t, dalloc_size(d));
if (cnids == NULL) {
return false;
}
for (i = 0; i < dalloc_size(d); i++) {
p = dalloc_get_object(d, i);
if (p == NULL) {
return NULL;
}
memcpy(&cnids[i], p, sizeof(uint64_t));
}
qsort(cnids, dalloc_size(d), sizeof(uint64_t), cnid_comp_fn);
slq->cnids = cnids;
slq->cnids_num = dalloc_size(d);
return true;
}
/**
* Allocate result handle used in the async Tracker cursor result
* handler for storing results
**/
static bool create_result_handle(struct sl_query *slq)
{
sl_nil_t nil = 0;
struct sl_rslts *query_results;
int result;
if (slq->query_results) {
DEBUG(1, ("unexpected existing result handle\n"));
return false;
}
query_results = talloc_zero(slq, struct sl_rslts);
if (query_results == NULL) {
return false;
}
/* CNIDs */
query_results->cnids = talloc_zero(query_results, sl_cnids_t);
if (query_results->cnids == NULL) {
return false;
}
query_results->cnids->ca_cnids = dalloc_new(query_results->cnids);
if (query_results->cnids->ca_cnids == NULL) {
return false;
}
query_results->cnids->ca_unkn1 = 0xadd;
if (slq->ctx2 > UINT32_MAX) {
DEBUG(1,("64bit ctx2 id too large: 0x%jx\n", (uintmax_t)slq->ctx2));
return false;
}
query_results->cnids->ca_context = (uint32_t)slq->ctx2;
/* FileMeta */
query_results->fm_array = dalloc_zero(query_results, sl_array_t);
if (query_results->fm_array == NULL) {
return false;
}
/* For some reason the list of results always starts with a nil entry */
result = dalloc_add_copy(query_results->fm_array, &nil, sl_nil_t);
if (result != 0) {
return false;
}
slq->query_results = query_results;
return true;
}
static bool add_results(sl_array_t *array, struct sl_query *slq)
{
sl_filemeta_t *fm;
uint64_t status;
int result;
bool ok;
/*
* Taken from network traces against a macOS SMB Spotlight server: if
* the search is not finished yet in the backend macOS returns 0x23,
* otherwise 0x0.
*/
if (slq->state >= SLQ_STATE_DONE) {
status = 0;
} else {
status = 0x23;
}
/* FileMeta */
fm = dalloc_zero(array, sl_filemeta_t);
if (fm == NULL) {
return false;
}
result = dalloc_add_copy(array, &status, uint64_t);
if (result != 0) {
return false;
}
result = dalloc_add(array, slq->query_results->cnids, sl_cnids_t);
if (result != 0) {
return false;
}
if (slq->query_results->num_results > 0) {
result = dalloc_add(fm, slq->query_results->fm_array, sl_array_t);
if (result != 0) {
return false;
}
}
result = dalloc_add(array, fm, sl_filemeta_t);
if (result != 0) {
return false;
}
/* This ensure the results get clean up after been sent to the client */
talloc_move(array, &slq->query_results);
ok = create_result_handle(slq);
if (!ok) {
DEBUG(1, ("couldn't add result handle\n"));
slq->state = SLQ_STATE_ERROR;
return false;
}
return true;
}
static const struct slrpc_cmd *slrpc_cmd_by_name(const char *rpccmd)
{
size_t i;
static const struct slrpc_cmd cmds[] = {
{ "fetchPropertiesForContext:", slrpc_fetch_properties},
{ "openQueryWithParams:forContext:", slrpc_open_query},
{ "fetchQueryResultsForContext:", slrpc_fetch_query_results},
{ "storeAttributes:forOIDArray:context:", slrpc_store_attributes},
{ "fetchAttributeNamesForOIDArray:context:", slrpc_fetch_attributenames},
{ "fetchAttributes:forOIDArray:context:", slrpc_fetch_attributes},
{ "fetchAllAttributes:forOIDArray:context:", slrpc_fetch_attributes},
{ "closeQueryForContext:", slrpc_close_query},
};
for (i = 0; i < ARRAY_SIZE(cmds); i++) {
int cmp;
cmp = strcmp(cmds[i].name, rpccmd);
if (cmp == 0) {
return &cmds[i];
}
}
return NULL;
}
/**
* Search the list of active queries given their context ids
**/
static struct sl_query *slq_for_ctx(struct mds_ctx *mds_ctx,
uint64_t ctx1, uint64_t ctx2)
{
struct sl_query *q;
for (q = mds_ctx->query_list; q; q = q->next) {
if ((q->ctx1 == ctx1) && (q->ctx2 == ctx2)) {
return q;
}
}
return NULL;
}
static int slq_destructor_cb(struct sl_query *slq)
{
SLQ_DEBUG(10, slq, "destroying");
/* Free all entries before freeing the slq handle! */
TALLOC_FREE(slq->entries_ctx);
TALLOC_FREE(slq->te);
if (slq->mds_ctx != NULL) {
DLIST_REMOVE(slq->mds_ctx->query_list, slq);
slq->mds_ctx = NULL;
}
TALLOC_FREE(slq->backend_private);
return 0;
}
/**
* Remove talloc_refcounted entry from mapping db
*
* Multiple queries (via the slq handle) may reference a
* sl_inode_path_map entry, when the last reference goes away as the
* queries are closed and this gets called to remove the entry from
* the db.
**/
static int ino_path_map_destr_cb(struct sl_inode_path_map *entry)
{
NTSTATUS status;
TDB_DATA key;
key = make_tdb_data((uint8_t *)&entry->ino, sizeof(entry->ino));
status = dbwrap_delete(entry->mds_ctx->ino_path_map, key);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(1, ("Failed to delete record: %s\n", nt_errstr(status)));
return -1;
}
DBG_DEBUG("deleted [0x%"PRIx64"] [%s]\n", entry->ino, entry->path);
return 0;
}
/**
* Add result to inode->path mapping dbwrap rbt db
*
* This is necessary as a CNID db substitute, ie we need a way to
* simulate unique, constant numerical identifiers for paths with an
* API that supports mapping from id to path.
*
* Entries are talloc'ed of the query, using talloc_reference() if
* multiple queries returned the same result. That way we can cleanup
* entries by calling talloc_free() on the query slq handles.
**/
static bool inode_map_add(struct sl_query *slq,
uint64_t ino,
const char *path,
struct stat_ex *st)
{
NTSTATUS status;
struct sl_inode_path_map *entry;
TDB_DATA key, value;
void *p;
key = make_tdb_data((uint8_t *)&ino, sizeof(ino));
status = dbwrap_fetch(slq->mds_ctx->ino_path_map, slq, key, &value);
if (NT_STATUS_IS_OK(status)) {
/*
* We have one db, so when different parallel queries
* return the same file, we have to refcount entries
* in the db.
*/
if (value.dsize != sizeof(void *)) {
DEBUG(1, ("invalid dsize\n"));
return false;
}
memcpy(&p, value.dptr, sizeof(p));
entry = talloc_get_type_abort(p, struct sl_inode_path_map);
DEBUG(10, ("map: %s\n", entry->path));
entry = talloc_reference(slq->entries_ctx, entry);
if (entry == NULL) {
DEBUG(1, ("talloc_reference failed\n"));
return false;
}
return true;
}
if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
DEBUG(1, ("dbwrap_fetch failed %s\n", nt_errstr(status)));
return false;
}
entry = talloc_zero(slq->entries_ctx, struct sl_inode_path_map);
if (entry == NULL) {
DEBUG(1, ("talloc failed\n"));
return false;
}
entry->ino = ino;
entry->mds_ctx = slq->mds_ctx;
entry->st = *st;
entry->path = talloc_strdup(entry, path);
if (entry->path == NULL) {
DEBUG(1, ("talloc failed\n"));
TALLOC_FREE(entry);
return false;
}
status = dbwrap_store(slq->mds_ctx->ino_path_map, key,
make_tdb_data((void *)&entry, sizeof(void *)), 0);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(1, ("Failed to store record: %s\n", nt_errstr(status)));
TALLOC_FREE(entry);
return false;
}
talloc_set_destructor(entry, ino_path_map_destr_cb);
return true;
}
bool mds_add_result(struct sl_query *slq, const char *path)
{
struct smb_filename *smb_fname = NULL;
const char *relative = NULL;
char *fake_path = NULL;
struct stat_ex sb;
uint64_t ino64;
int result;
NTSTATUS status;
bool sub;
bool ok;
/*
* We're in a tevent callback which means in the case of
* running as external RPC service we're running as root and
* not as the user.
*/
if (!become_authenticated_pipe_user(slq->mds_ctx->pipe_session_info)) {
DBG_ERR("can't become authenticated user: %d\n",
slq->mds_ctx->uid);
smb_panic("can't become authenticated user");
}
if (geteuid() != slq->mds_ctx->uid) {
DBG_ERR("uid mismatch: %d/%d\n", geteuid(), slq->mds_ctx->uid);
smb_panic("uid mismatch");
}
/*
* We've changed identity to the authenticated pipe user, so
* any function exit below must ensure we switch back
*/
status = synthetic_pathref(talloc_tos(),
slq->mds_ctx->conn->cwd_fsp,
path,
NULL,
NULL,
0,
0,
&smb_fname);
if (!NT_STATUS_IS_OK(status)) {
DBG_DEBUG("synthetic_pathref [%s]: %s\n",
smb_fname_str_dbg(smb_fname),
nt_errstr(status));
unbecome_authenticated_pipe_user();
return true;
}
sb = smb_fname->st;
status = smbd_check_access_rights_fsp(slq->mds_ctx->conn->cwd_fsp,
smb_fname->fsp,
false,
FILE_READ_DATA);
unbecome_authenticated_pipe_user();
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(smb_fname);
return true;
}
/* Done with smb_fname now. */
TALLOC_FREE(smb_fname);
ino64 = SMB_VFS_FS_FILE_ID(slq->mds_ctx->conn, &sb);
if (slq->cnids) {
bool found;
/*
* Check whether the found element is in the requested
* set of IDs. Note that we're faking CNIDs by using
* filesystem inode numbers here
*/
found = bsearch(&ino64,
slq->cnids,
slq->cnids_num,
sizeof(uint64_t),
cnid_comp_fn);
if (!found) {
return true;
}
}
sub = subdir_of(slq->mds_ctx->spath,
slq->mds_ctx->spath_len,
path,
&relative);
if (!sub) {
DBG_ERR("[%s] is not inside [%s]\n",
path, slq->mds_ctx->spath);
slq->state = SLQ_STATE_ERROR;
return false;
}
/*
* Add inode number and filemeta to result set, this is what
* we return as part of the result set of a query
*/
result = dalloc_add_copy(slq->query_results->cnids->ca_cnids,
&ino64,
uint64_t);
if (result != 0) {
DBG_ERR("dalloc error\n");
slq->state = SLQ_STATE_ERROR;
return false;
}
fake_path = talloc_asprintf(slq,
"/%s/%s",
slq->mds_ctx->sharename,
relative);
if (fake_path == NULL) {
slq->state = SLQ_STATE_ERROR;
return false;
}
ok = add_filemeta(slq->mds_ctx,
slq->reqinfo,
slq->query_results->fm_array,
fake_path,
&sb);
if (!ok) {
DBG_ERR("add_filemeta error\n");
TALLOC_FREE(fake_path);
slq->state = SLQ_STATE_ERROR;
return false;
}
ok = inode_map_add(slq, ino64, fake_path, &sb);
TALLOC_FREE(fake_path);
if (!ok) {
DEBUG(1, ("inode_map_add error\n"));
slq->state = SLQ_STATE_ERROR;
return false;
}
slq->query_results->num_results++;
return true;
}
/***********************************************************
* Spotlight RPC functions
***********************************************************/
static bool slrpc_fetch_properties(struct mds_ctx *mds_ctx,
const DALLOC_CTX *query, DALLOC_CTX *reply)
{
sl_dict_t *dict;
sl_array_t *array;
char *s;
uint64_t u;
sl_bool_t b;
sl_uuid_t uuid;
int result;
dict = dalloc_zero(reply, sl_dict_t);
if (dict == NULL) {
return false;
}
/* kMDSStoreHasPersistentUUID = false */
result = dalloc_stradd(dict, "kMDSStoreHasPersistentUUID");
if (result != 0) {
return false;
}
b = false;
result = dalloc_add_copy(dict, &b, sl_bool_t);
if (result != 0) {
return false;
}
/* kMDSStoreIsBackup = false */
result = dalloc_stradd(dict, "kMDSStoreIsBackup");
if (result != 0) {
return false;
}
b = false;
result = dalloc_add_copy(dict, &b, sl_bool_t);
if (result != 0) {
return false;
}
/* kMDSStoreUUID = uuid */
result = dalloc_stradd(dict, "kMDSStoreUUID");
if (result != 0) {
return false;
}
memcpy(uuid.sl_uuid, "fakeuuidfakeuuid", sizeof(uuid.sl_uuid));
result = dalloc_add_copy(dict, &uuid, sl_uuid_t);
if (result != 0) {
return false;
}
/* kMDSStoreSupportsVolFS = true */
result = dalloc_stradd(dict, "kMDSStoreSupportsVolFS");
if (result != 0) {
return false;
}
b = true;
result = dalloc_add_copy(dict, &b, sl_bool_t);
if (result != 0) {
return false;
}
/* kMDSVolumeUUID = uuid */
result = dalloc_stradd(dict, "kMDSVolumeUUID");
if (result != 0) {
return false;
}
memcpy(uuid.sl_uuid, "fakeuuidfakeuuid", sizeof(uuid.sl_uuid));
result = dalloc_add_copy(dict, &uuid, sl_uuid_t);
if (result != 0) {
return false;
}
/* kMDSDiskStoreSpindleNumber = 1 (fake) */
result = dalloc_stradd(dict, "kMDSDiskStoreSpindleNumber");
if (result != 0) {
return false;
}
u = 1;
result = dalloc_add_copy(dict, &u, uint64_t);
if (result != 0) {
return false;
}
/* kMDSDiskStorePolicy = 3 (whatever that means, taken from OS X) */
result = dalloc_stradd(dict, "kMDSDiskStorePolicy");
if (result != 0) {
return false;
}
u = 3;
result = dalloc_add_copy(dict, &u, uint64_t);
if (result != 0) {
return false;
}
/* kMDSStoreMetaScopes array */
result = dalloc_stradd(dict, "kMDSStoreMetaScopes");
if (result != 0) {
return false;
}
array = dalloc_zero(dict, sl_array_t);
if (array == NULL) {
return NULL;
}
result = dalloc_stradd(array, "kMDQueryScopeComputer");
if (result != 0) {
return false;
}
result = dalloc_stradd(array, "kMDQueryScopeAllIndexed");
if (result != 0) {
return false;
}
result = dalloc_stradd(array, "kMDQueryScopeComputerIndexed");
if (result != 0) {
return false;
}
result = dalloc_add(dict, array, sl_array_t);
if (result != 0) {
return false;
}
/* kMDSStoreDevice = 0x1000003 (whatever that means, taken from OS X) */
result = dalloc_stradd(dict, "kMDSStoreDevice");
if (result != 0) {
return false;
}
u = 0x1000003;
result = dalloc_add_copy(dict, &u, uint64_t);
if (result != 0) {
return false;
}
/* kMDSStoreSupportsTCC = true (whatever that means, taken from OS X) */
result = dalloc_stradd(dict, "kMDSStoreSupportsTCC");
if (result != 0) {
return false;
}
b = true;
result = dalloc_add_copy(dict, &b, sl_bool_t);
if (result != 0) {
return false;
}
/* kMDSStorePathScopes = ["/"] (whatever that means, taken from OS X) */
result = dalloc_stradd(dict, "kMDSStorePathScopes");
if (result != 0) {
return false;
}
array = dalloc_zero(dict, sl_array_t);
if (array == NULL) {
return false;
}
s = talloc_strdup(dict, "/");
if (s == NULL) {
return false;
}
talloc_set_name(s, "smb_ucs2_t *");
result = dalloc_add(array, s, smb_ucs2_t *);
if (result != 0) {
return false;
}
result = dalloc_add(dict, array, sl_array_t);
if (result != 0) {
return false;
}
result = dalloc_add(reply, dict, sl_dict_t);
if (result != 0) {
return false;
}
return true;
}
static void slq_close_timer(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval current_time,
void *private_data)
{
struct sl_query *slq = talloc_get_type_abort(
private_data, struct sl_query);
struct mds_ctx *mds_ctx = slq->mds_ctx;
SLQ_DEBUG(10, slq, "expired");
TALLOC_FREE(slq);
if (CHECK_DEBUGLVL(10)) {
for (slq = mds_ctx->query_list; slq != NULL; slq = slq->next) {
SLQ_DEBUG(10, slq, "pending");
}
}
}
/**
* Translate a fake scope from the client like /sharename/dir
* to the real server-side path, replacing the "/sharename" part
* with the absolute server-side path of the share.
**/
static bool mdssvc_real_scope(struct sl_query *slq, const char *fake_scope)
{
size_t sname_len = strlen(slq->mds_ctx->sharename);
size_t fake_scope_len = strlen(fake_scope);
if (fake_scope_len < sname_len + 1) {
DBG_ERR("Short scope [%s] for share [%s]\n",
fake_scope, slq->mds_ctx->sharename);
return false;
}
slq->path_scope = talloc_asprintf(slq,
"%s%s",
slq->mds_ctx->spath,
fake_scope + sname_len + 1);
if (slq->path_scope == NULL) {
return false;
}
return true;
}
/**
* Begin a search query
**/
static bool slrpc_open_query(struct mds_ctx *mds_ctx,
const DALLOC_CTX *query, DALLOC_CTX *reply)
{
bool ok;
uint64_t sl_result;
uint64_t *uint64p;
DALLOC_CTX *reqinfo;
sl_array_t *array, *path_scope;
sl_cnids_t *cnids;
struct sl_query *slq = NULL;
int result;
const char *querystring = NULL;
size_t querystring_len;
char *dest = NULL;
size_t dest_remaining;
size_t nconv;
char *scope = NULL;
array = dalloc_zero(reply, sl_array_t);
if (array == NULL) {
return false;
}
/* Allocate and initialize query object */
slq = talloc_zero(mds_ctx, struct sl_query);
if (slq == NULL) {
return false;
}
slq->entries_ctx = talloc_named_const(slq, 0, "struct sl_query.entries_ctx");
if (slq->entries_ctx == NULL) {
TALLOC_FREE(slq);
return false;
}
talloc_set_destructor(slq, slq_destructor_cb);
slq->state = SLQ_STATE_NEW;
slq->mds_ctx = mds_ctx;
slq->last_used = timeval_current();
slq->start_time = slq->last_used;
slq->expire_time = timeval_add(&slq->last_used, MAX_SL_RUNTIME, 0);
slq->te = tevent_add_timer(global_event_context(), slq,
slq->expire_time, slq_close_timer, slq);
if (slq->te == NULL) {
DEBUG(1, ("tevent_add_timer failed\n"));
goto error;
}
querystring = dalloc_value_for_key(query, "DALLOC_CTX", 0,
"DALLOC_CTX", 1,
"kMDQueryString",
"char *");
if (querystring == NULL) {
DEBUG(1, ("missing kMDQueryString\n"));
goto error;
}
querystring_len = talloc_array_length(querystring);
slq->query_string = talloc_array(slq, char, querystring_len);
if (slq->query_string == NULL) {
DEBUG(1, ("out of memory\n"));
goto error;
}
dest = slq->query_string;
dest_remaining = talloc_array_length(dest);
nconv = smb_iconv(mds_ctx->ic_nfd_to_nfc,
&querystring,
&querystring_len,
&dest,
&dest_remaining);
if (nconv == (size_t)-1) {
DBG_ERR("smb_iconv failed for: %s\n", querystring);
return false;
}
uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
"uint64_t", 1);
if (uint64p == NULL) {
goto error;
}
slq->ctx1 = *uint64p;
uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
"uint64_t", 2);
if (uint64p == NULL) {
goto error;
}
slq->ctx2 = *uint64p;
path_scope = dalloc_value_for_key(query, "DALLOC_CTX", 0,
"DALLOC_CTX", 1,
"kMDScopeArray",
"sl_array_t");
if (path_scope == NULL) {
DBG_ERR("missing kMDScopeArray\n");
goto error;
}
scope = dalloc_get(path_scope, "char *", 0);
if (scope == NULL) {
scope = dalloc_get(path_scope,
"DALLOC_CTX", 0,
"char *", 0);
}
if (scope == NULL) {
DBG_ERR("Failed to parse kMDScopeArray\n");
goto error;
}
ok = mdssvc_real_scope(slq, scope);
if (!ok) {
goto error;
}
reqinfo = dalloc_value_for_key(query, "DALLOC_CTX", 0,
"DALLOC_CTX", 1,
"kMDAttributeArray",
"sl_array_t");
if (reqinfo == NULL) {
DBG_ERR("missing kMDAttributeArray\n");
goto error;
}
slq->reqinfo = talloc_steal(slq, reqinfo);
DEBUG(10, ("requested attributes: %s", dalloc_dump(reqinfo, 0)));
cnids = dalloc_value_for_key(query, "DALLOC_CTX", 0,
"DALLOC_CTX", 1,
"kMDQueryItemArray",
"sl_array_t");
if (cnids) {
ok = sort_cnids(slq, cnids->ca_cnids);
if (!ok) {
goto error;
}
}
ok = create_result_handle(slq);
if (!ok) {
DEBUG(1, ("create_result_handle error\n"));
slq->state = SLQ_STATE_ERROR;
goto error;
}
SLQ_DEBUG(10, slq, "new");
DLIST_ADD(mds_ctx->query_list, slq);
ok = mds_ctx->backend->search_start(slq);
if (!ok) {
DBG_ERR("backend search_start failed\n");
goto error;
}
sl_result = 0;
result = dalloc_add_copy(array, &sl_result, uint64_t);
if (result != 0) {
goto error;
}
result = dalloc_add(reply, array, sl_array_t);
if (result != 0) {
goto error;
}
return true;
error:
sl_result = UINT64_MAX;
TALLOC_FREE(slq);
result = dalloc_add_copy(array, &sl_result, uint64_t);
if (result != 0) {
return false;
}
result = dalloc_add(reply, array, sl_array_t);
if (result != 0) {
return false;
}
return true;
}
/**
* Fetch results of a query
**/
static bool slrpc_fetch_query_results(struct mds_ctx *mds_ctx,
const DALLOC_CTX *query,
DALLOC_CTX *reply)
{
bool ok;
struct sl_query *slq = NULL;
uint64_t *uint64p, ctx1, ctx2;
uint64_t status;
sl_array_t *array;
int result;
array = dalloc_zero(reply, sl_array_t);
if (array == NULL) {
return false;
}
/* Get query for context */
uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
"uint64_t", 1);
if (uint64p == NULL) {
goto error;
}
ctx1 = *uint64p;
uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
"uint64_t", 2);
if (uint64p == NULL) {
goto error;
}
ctx2 = *uint64p;
slq = slq_for_ctx(mds_ctx, ctx1, ctx2);
if (slq == NULL) {
DEBUG(1, ("bad context: [0x%jx,0x%jx]\n",
(uintmax_t)ctx1, (uintmax_t)ctx2));
goto error;
}
TALLOC_FREE(slq->te);
slq->last_used = timeval_current();
slq->expire_time = timeval_add(&slq->last_used, MAX_SL_RUNTIME, 0);
slq->te = tevent_add_timer(global_event_context(), slq,
slq->expire_time, slq_close_timer, slq);
if (slq->te == NULL) {
DEBUG(1, ("tevent_add_timer failed\n"));
goto error;
}
SLQ_DEBUG(10, slq, "fetch");
switch (slq->state) {
case SLQ_STATE_RUNNING:
case SLQ_STATE_RESULTS:
case SLQ_STATE_FULL:
case SLQ_STATE_DONE:
ok = add_results(array, slq);
if (!ok) {
DEBUG(1, ("error adding results\n"));
goto error;
}
if (slq->state == SLQ_STATE_FULL) {
slq->state = SLQ_STATE_RUNNING;
slq->mds_ctx->backend->search_cont(slq);
}
break;
case SLQ_STATE_ERROR:
DEBUG(1, ("query in error state\n"));
goto error;
default:
DEBUG(1, ("unexpected query state %d\n", slq->state));
goto error;
}
result = dalloc_add(reply, array, sl_array_t);
if (result != 0) {
goto error;
}
return true;
error:
status = UINT64_MAX;
TALLOC_FREE(slq);
result = dalloc_add_copy(array, &status, uint64_t);
if (result != 0) {
return false;
}
result = dalloc_add(reply, array, sl_array_t);
if (result != 0) {
return false;
}
return true;
}
/**
* Store metadata attributes for a CNID
**/
static bool slrpc_store_attributes(struct mds_ctx *mds_ctx,
const DALLOC_CTX *query, DALLOC_CTX *reply)
{
uint64_t sl_result;
sl_array_t *array;
int result;
array = dalloc_zero(reply, sl_array_t);
if (array == NULL) {
return false;
}
/*
* FIXME: not implemented. Used by the client for eg setting
* the modification date of the shared directory which clients
* poll indicating changes on the share and cause the client
* to refresh view.
*/
sl_result = 0;
result = dalloc_add_copy(array, &sl_result, uint64_t);
if (result != 0) {
return false;
}
result = dalloc_add(reply, array, sl_array_t);
if (result != 0) {
return false;
}
return true;
}
/**
* Fetch supported metadata attributes for a CNID
**/
static bool slrpc_fetch_attributenames(struct mds_ctx *mds_ctx,
const DALLOC_CTX *query,
DALLOC_CTX *reply)
{
uint64_t id;
sl_cnids_t *cnids;
sl_array_t *array;
uint64_t sl_result;
sl_cnids_t *replycnids;
sl_array_t *mdattrs;
sl_filemeta_t *fmeta;
int result;
void *p;
cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 1);
if (cnids == NULL) {
return false;
}
p = dalloc_get_object(cnids->ca_cnids, 0);
if (p == NULL) {
return NULL;
}
memcpy(&id, p, sizeof(uint64_t));
/* Result array */
array = dalloc_zero(reply, sl_array_t);
if (array == NULL) {
return false;
}
result = dalloc_add(reply, array, sl_array_t);
if (result != 0) {
return false;
}
/* Return result value 0 */
sl_result = 0;
result = dalloc_add_copy(array, &sl_result, uint64_t);
if (result != 0) {
return false;
}
/* Return CNID array */
replycnids = talloc_zero(reply, sl_cnids_t);
if (replycnids == NULL) {
return false;
}
replycnids->ca_cnids = dalloc_new(cnids);
if (replycnids->ca_cnids == NULL) {
return false;
}
replycnids->ca_unkn1 = 0xfec;
replycnids->ca_context = cnids->ca_context;
result = dalloc_add_copy(replycnids->ca_cnids, &id, uint64_t);
if (result != 0) {
return false;
}
result = dalloc_add(array, replycnids, sl_cnids_t);
if (result != 0) {
return false;
}
/*
* FIXME: this should return the real attributes from all
* known metadata sources (Tracker and filesystem)
*/
mdattrs = dalloc_zero(reply, sl_array_t);
if (mdattrs == NULL) {
return false;
}
result = dalloc_stradd(mdattrs, "kMDItemFSName");
if (result != 0) {
return false;
}
result = dalloc_stradd(mdattrs, "kMDItemDisplayName");
if (result != 0) {
return false;
}
result = dalloc_stradd(mdattrs, "kMDItemFSSize");
if (result != 0) {
return false;
}
result = dalloc_stradd(mdattrs, "kMDItemFSOwnerUserID");
if (result != 0) {
return false;
}
result = dalloc_stradd(mdattrs, "kMDItemFSOwnerGroupID");
if (result != 0) {
return false;
}
result = dalloc_stradd(mdattrs, "kMDItemFSContentChangeDate");
if (result != 0) {
return false;
}
fmeta = dalloc_zero(reply, sl_filemeta_t);
if (fmeta == NULL) {
return false;
}
result = dalloc_add(fmeta, mdattrs, sl_array_t);
if (result != 0) {
return false;
}
result = dalloc_add(array, fmeta, sl_filemeta_t);
if (result != 0) {
return false;
}
return true;
}
/**
* Fetch metadata attribute values for a CNID
**/
static bool slrpc_fetch_attributes(struct mds_ctx *mds_ctx,
const DALLOC_CTX *query, DALLOC_CTX *reply)
{
int result;
bool ok;
sl_array_t *array;
sl_cnids_t *cnids;
sl_cnids_t *replycnids;
sl_array_t *reqinfo;
uint64_t ino;
uint64_t sl_result;
sl_filemeta_t *fm;
sl_array_t *fm_array;
sl_nil_t nil;
char *path = NULL;
struct smb_filename *smb_fname = NULL;
struct stat_ex *sp = NULL;
struct sl_inode_path_map *elem = NULL;
void *p;
TDB_DATA val = tdb_null;
NTSTATUS status;
array = dalloc_zero(reply, sl_array_t);
if (array == NULL) {
return false;
}
replycnids = talloc_zero(reply, sl_cnids_t);
if (replycnids == NULL) {
goto error;
}
replycnids->ca_cnids = dalloc_new(replycnids);
if (replycnids->ca_cnids == NULL) {
goto error;
}
fm = dalloc_zero(array, sl_filemeta_t);
if (fm == NULL) {
goto error;
}
fm_array = dalloc_zero(fm, sl_array_t);
if (fm_array == NULL) {
goto error;
}
/* For some reason the list of results always starts with a nil entry */
result = dalloc_add_copy(fm_array, &nil, sl_nil_t);
if (result == -1) {
goto error;
}
reqinfo = dalloc_get(query, "DALLOC_CTX", 0, "sl_array_t", 1);
if (reqinfo == NULL) {
goto error;
}
cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 2);
if (cnids == NULL) {
goto error;
}
p = dalloc_get_object(cnids->ca_cnids, 0);
if (p == NULL) {
goto error;
}
memcpy(&ino, p, sizeof(uint64_t));
replycnids->ca_unkn1 = 0xfec;
replycnids->ca_context = cnids->ca_context;
result = dalloc_add_copy(replycnids->ca_cnids, &ino, uint64_t);
if (result != 0) {
goto error;
}
status = dbwrap_fetch(mds_ctx->ino_path_map, reply,
make_tdb_data((void*)&ino, sizeof(uint64_t)),
&val);
if (NT_STATUS_IS_OK(status)) {
if (val.dsize != sizeof(p)) {
DBG_ERR("invalid record pointer size: %zd\n", val.dsize);
TALLOC_FREE(val.dptr);
goto error;
}
memcpy(&p, val.dptr, sizeof(p));
elem = talloc_get_type_abort(p, struct sl_inode_path_map);
path = elem->path;
sp = &elem->st;
}
ok = add_filemeta(mds_ctx, reqinfo, fm_array, path, sp);
if (!ok) {
goto error;
}
sl_result = 0;
result = dalloc_add_copy(array, &sl_result, uint64_t);
if (result != 0) {
goto error;
}
result = dalloc_add(array, replycnids, sl_cnids_t);
if (result != 0) {
goto error;
}
result = dalloc_add(fm, fm_array, sl_array_t);
if (result != 0) {
goto error;
}
result = dalloc_add(array, fm, sl_filemeta_t);
if (result != 0) {
goto error;
}
result = dalloc_add(reply, array, sl_array_t);
if (result != 0) {
goto error;
}
TALLOC_FREE(smb_fname);
return true;
error:
TALLOC_FREE(smb_fname);
sl_result = UINT64_MAX;
result = dalloc_add_copy(array, &sl_result, uint64_t);
if (result != 0) {
return false;
}
result = dalloc_add(reply, array, sl_array_t);
if (result != 0) {
return false;
}
return true;
}
/**
* Close a query
**/
static bool slrpc_close_query(struct mds_ctx *mds_ctx,
const DALLOC_CTX *query, DALLOC_CTX *reply)
{
struct sl_query *slq = NULL;
uint64_t *uint64p, ctx1, ctx2;
sl_array_t *array;
uint64_t sl_res;
int result;
array = dalloc_zero(reply, sl_array_t);
if (array == NULL) {
return false;
}
/* Context */
uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
"uint64_t", 1);
if (uint64p == NULL) {
goto done;
}
ctx1 = *uint64p;
uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
"uint64_t", 2);
if (uint64p == NULL) {
goto done;
}
ctx2 = *uint64p;
/* Get query for context and free it */
slq = slq_for_ctx(mds_ctx, ctx1, ctx2);
if (slq == NULL) {
DEBUG(1, ("bad context: [0x%jx,0x%jx]\n",
(uintmax_t)ctx1, (uintmax_t)ctx2));
goto done;
}
SLQ_DEBUG(10, slq, "close");
TALLOC_FREE(slq);
done:
sl_res = UINT64_MAX;
result = dalloc_add_copy(array, &sl_res, uint64_t);
if (result != 0) {
return false;
}
result = dalloc_add(reply, array, sl_array_t);
if (result != 0) {
return false;
}
return true;
}
static struct mdssvc_ctx *mdssvc_init(struct tevent_context *ev)
{
bool ok;
if (mdssvc_ctx != NULL) {
return mdssvc_ctx;
}
mdssvc_ctx = talloc_zero(ev, struct mdssvc_ctx);
if (mdssvc_ctx == NULL) {
return NULL;
}
mdssvc_ctx->ev_ctx = ev;
ok = mdsscv_backend_noindex.init(mdssvc_ctx);
if (!ok) {
DBG_ERR("backend init failed\n");
TALLOC_FREE(mdssvc_ctx);
return NULL;
}
#ifdef HAVE_SPOTLIGHT_BACKEND_ES
ok = mdsscv_backend_es.init(mdssvc_ctx);
if (!ok) {
DBG_ERR("backend init failed\n");
TALLOC_FREE(mdssvc_ctx);
return NULL;
}
#endif
#ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
ok = mdsscv_backend_tracker.init(mdssvc_ctx);
if (!ok) {
DBG_ERR("backend init failed\n");
TALLOC_FREE(mdssvc_ctx);
return NULL;
}
#endif
return mdssvc_ctx;
}
/**
* Init callbacks at startup
*
* This gets typically called in the main parent smbd which means we can't
* initialize our global state here.
**/
bool mds_init(struct messaging_context *msg_ctx)
{
return true;
}
bool mds_shutdown(void)
{
bool ok;
if (mdssvc_ctx == NULL) {
return false;
}
ok = mdsscv_backend_noindex.shutdown(mdssvc_ctx);
if (!ok) {
goto fail;
}
#ifdef HAVE_SPOTLIGHT_BACKEND_ES
ok = mdsscv_backend_es.shutdown(mdssvc_ctx);
if (!ok) {
goto fail;
}
#endif
#ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
ok = mdsscv_backend_tracker.shutdown(mdssvc_ctx);
if (!ok) {
goto fail;
}
#endif
ok = true;
fail:
TALLOC_FREE(mdssvc_ctx);
return ok;
}
/**
* Tear down connections and free all resources
**/
static int mds_ctx_destructor_cb(struct mds_ctx *mds_ctx)
{
/*
* We need to free query_list before ino_path_map
*/
while (mds_ctx->query_list != NULL) {
/*
* slq destructor removes element from list.
* Don't use TALLOC_FREE()!
*/
talloc_free(mds_ctx->query_list);
}
TALLOC_FREE(mds_ctx->ino_path_map);
if (mds_ctx->conn != NULL) {
SMB_VFS_DISCONNECT(mds_ctx->conn);
conn_free(mds_ctx->conn);
}
ZERO_STRUCTP(mds_ctx);
return 0;
}
/**
* Initialise a context per RPC bind
*
* This ends up being called for every tcon, because the client does a
* RPC bind for every tcon, so this is actually a per tcon context.
**/
NTSTATUS mds_init_ctx(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct messaging_context *msg_ctx,
struct auth_session_info *session_info,
int snum,
const char *sharename,
const char *path,
struct mds_ctx **_mds_ctx)
{
const struct loadparm_substitution *lp_sub =
loadparm_s3_global_substitution();
struct smb_filename conn_basedir;
struct mds_ctx *mds_ctx;
int backend;
int ret;
bool ok;
smb_iconv_t iconv_hnd = (smb_iconv_t)-1;
NTSTATUS status;
if (!lp_spotlight(snum)) {
return NT_STATUS_WRONG_VOLUME;
}
mds_ctx = talloc_zero(mem_ctx, struct mds_ctx);
if (mds_ctx == NULL) {
return NT_STATUS_NO_MEMORY;
}
talloc_set_destructor(mds_ctx, mds_ctx_destructor_cb);
mds_ctx->mdssvc_ctx = mdssvc_init(ev);
if (mds_ctx->mdssvc_ctx == NULL) {
return NT_STATUS_NO_MEMORY;
}
backend = lp_spotlight_backend(snum);
switch (backend) {
case SPOTLIGHT_BACKEND_NOINDEX:
mds_ctx->backend = &mdsscv_backend_noindex;
break;
#ifdef HAVE_SPOTLIGHT_BACKEND_ES
case SPOTLIGHT_BACKEND_ES:
mds_ctx->backend = &mdsscv_backend_es;
break;
#endif
#ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
case SPOTLIGHT_BACKEND_TRACKER:
mds_ctx->backend = &mdsscv_backend_tracker;
break;
#endif
default:
DBG_ERR("Unknown backend %d\n", backend);
TALLOC_FREE(mdssvc_ctx);
status = NT_STATUS_INTERNAL_ERROR;
goto error;
}
iconv_hnd = smb_iconv_open_ex(mds_ctx,
"UTF8-NFD",
"UTF8-NFC",
false);
if (iconv_hnd == (smb_iconv_t)-1) {
status = NT_STATUS_INTERNAL_ERROR;
goto error;
}
mds_ctx->ic_nfc_to_nfd = iconv_hnd;
iconv_hnd = smb_iconv_open_ex(mds_ctx,
"UTF8-NFC",
"UTF8-NFD",
false);
if (iconv_hnd == (smb_iconv_t)-1) {
status = NT_STATUS_INTERNAL_ERROR;
goto error;
}
mds_ctx->ic_nfd_to_nfc = iconv_hnd;
mds_ctx->sharename = talloc_strdup(mds_ctx, sharename);
if (mds_ctx->sharename == NULL) {
status = NT_STATUS_NO_MEMORY;
goto error;
}
mds_ctx->spath = talloc_strdup(mds_ctx, path);
if (mds_ctx->spath == NULL) {
status = NT_STATUS_NO_MEMORY;
goto error;
}
mds_ctx->spath_len = strlen(path);
mds_ctx->snum = snum;
mds_ctx->pipe_session_info = session_info;
if (session_info->security_token->num_sids < 1) {
status = NT_STATUS_BAD_LOGON_SESSION_STATE;
goto error;
}
sid_copy(&mds_ctx->sid, &session_info->security_token->sids[0]);
mds_ctx->uid = session_info->unix_token->uid;
mds_ctx->ino_path_map = db_open_rbt(mds_ctx);
if (mds_ctx->ino_path_map == NULL) {
DEBUG(1,("open inode map db failed\n"));
status = NT_STATUS_INTERNAL_ERROR;
goto error;
}
status = create_conn_struct_cwd(mds_ctx,
ev,
msg_ctx,
session_info,
snum,
lp_path(talloc_tos(), lp_sub, snum),
&mds_ctx->conn);
if (!NT_STATUS_IS_OK(status)) {
DBG_ERR("failed to create conn for vfs: %s\n",
nt_errstr(status));
goto error;
}
conn_basedir = (struct smb_filename) {
.base_name = mds_ctx->conn->connectpath,
};
ret = vfs_ChDir(mds_ctx->conn, &conn_basedir);
if (ret != 0) {
DBG_ERR("vfs_ChDir [%s] failed: %s\n",
conn_basedir.base_name, strerror(errno));
status = map_nt_error_from_unix(errno);
goto error;
}
ok = mds_ctx->backend->connect(mds_ctx);
if (!ok) {
DBG_ERR("backend connect failed\n");
status = NT_STATUS_CONNECTION_RESET;
goto error;
}
*_mds_ctx = mds_ctx;
return NT_STATUS_OK;
error:
if (mds_ctx->ic_nfc_to_nfd != NULL) {
smb_iconv_close(mds_ctx->ic_nfc_to_nfd);
}
if (mds_ctx->ic_nfd_to_nfc != NULL) {
smb_iconv_close(mds_ctx->ic_nfd_to_nfc);
}
TALLOC_FREE(mds_ctx);
return status;
}
/**
* Dispatch a Spotlight RPC command
**/
bool mds_dispatch(struct mds_ctx *mds_ctx,
struct mdssvc_blob *request_blob,
struct mdssvc_blob *response_blob,
size_t max_fragment_size)
{
bool ok;
int ret;
DALLOC_CTX *query = NULL;
DALLOC_CTX *reply = NULL;
char *rpccmd;
const struct slrpc_cmd *slcmd;
const struct smb_filename conn_basedir = {
.base_name = mds_ctx->conn->connectpath,
};
NTSTATUS status;
if (CHECK_DEBUGLVL(10)) {
const struct sl_query *slq;
for (slq = mds_ctx->query_list; slq != NULL; slq = slq->next) {
SLQ_DEBUG(10, slq, "pending");
}
}
response_blob->length = 0;
DEBUG(10, ("share path: %s\n", mds_ctx->spath));
query = dalloc_new(mds_ctx);
if (query == NULL) {
ok = false;
goto cleanup;
}
reply = dalloc_new(mds_ctx);
if (reply == NULL) {
ok = false;
goto cleanup;
}
ok = sl_unpack(query, (char *)request_blob->spotlight_blob,
request_blob->length);
if (!ok) {
DEBUG(1, ("error unpacking Spotlight RPC blob\n"));
goto cleanup;
}
DEBUG(5, ("%s", dalloc_dump(query, 0)));
rpccmd = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
"char *", 0);
if (rpccmd == NULL) {
DEBUG(1, ("missing primary Spotlight RPC command\n"));
ok = false;
goto cleanup;
}
DEBUG(10, ("Spotlight RPC cmd: %s\n", rpccmd));
slcmd = slrpc_cmd_by_name(rpccmd);
if (slcmd == NULL) {
DEBUG(1, ("unsupported primary Spotlight RPC command %s\n",
rpccmd));
ok = false;
goto cleanup;
}
ret = vfs_ChDir(mds_ctx->conn, &conn_basedir);
if (ret != 0) {
DBG_ERR("vfs_ChDir [%s] failed: %s\n",
conn_basedir.base_name, strerror(errno));
ok = false;
goto cleanup;
}
ok = slcmd->function(mds_ctx, query, reply);
if (!ok) {
goto cleanup;
}
DBG_DEBUG("%s", dalloc_dump(reply, 0));
status = sl_pack_alloc(response_blob,
reply,
response_blob,
max_fragment_size);
if (!NT_STATUS_IS_OK(status)) {
DBG_ERR("sl_pack_alloc() failed\n");
goto cleanup;
}
cleanup:
talloc_free(query);
talloc_free(reply);
return ok;
}