summaryrefslogtreecommitdiffstats
path: root/source3/rpc_server/mdssvc/mdssvc.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/rpc_server/mdssvc/mdssvc.c')
-rw-r--r--source3/rpc_server/mdssvc/mdssvc.c1893
1 files changed, 1893 insertions, 0 deletions
diff --git a/source3/rpc_server/mdssvc/mdssvc.c b/source3/rpc_server/mdssvc/mdssvc.c
new file mode 100644
index 0000000..5f0ec02
--- /dev/null
+++ b/source3/rpc_server/mdssvc/mdssvc.c
@@ -0,0 +1,1893 @@
+/*
+ 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", (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 acually 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;
+}