2 Unix SMB/CIFS implementation.
3 Main metadata server / Spotlight routines
5 Copyright (C) Ralph Boehme 2012-2014
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "smbd/proto.h"
23 #include "librpc/gen_ndr/auth.h"
24 #include "dbwrap/dbwrap.h"
25 #include "lib/util/dlinklist.h"
26 #include "lib/util/util_tdb.h"
27 #include "lib/util/time_basic.h"
28 #include "lib/dbwrap/dbwrap_rbt.h"
29 #include "libcli/security/dom_sid.h"
30 #include "libcli/security/security.h"
32 #include "mdssvc_noindex.h"
33 #ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
34 #include "mdssvc_tracker.h"
36 #ifdef HAVE_SPOTLIGHT_BACKEND_ES
37 #include "mdssvc_es.h"
39 #include "lib/global_contexts.h"
42 #define DBGC_CLASS DBGC_RPC_SRV
46 bool (*function)(struct mds_ctx *mds_ctx,
47 const DALLOC_CTX *query,
51 struct slq_destroy_state {
52 struct tevent_context *ev;
57 * This is a static global because we may be called multiple times and
58 * we only want one mdssvc_ctx per connection to Tracker.
60 * The client will bind multiple times to the mdssvc RPC service, once
61 * for every tree connect.
63 static struct mdssvc_ctx *mdssvc_ctx = NULL;
66 * If these functions return an error, they hit something like a non
67 * recoverable talloc error. Most errors are dealt with by returning
68 * an error code in the Spotlight RPC reply.
70 static bool slrpc_fetch_properties(struct mds_ctx *mds_ctx,
71 const DALLOC_CTX *query, DALLOC_CTX *reply);
72 static bool slrpc_open_query(struct mds_ctx *mds_ctx,
73 const DALLOC_CTX *query, DALLOC_CTX *reply);
74 static bool slrpc_fetch_query_results(struct mds_ctx *mds_ctx,
75 const DALLOC_CTX *query, DALLOC_CTX *reply);
76 static bool slrpc_store_attributes(struct mds_ctx *mds_ctx,
77 const DALLOC_CTX *query, DALLOC_CTX *reply);
78 static bool slrpc_fetch_attributenames(struct mds_ctx *mds_ctx,
79 const DALLOC_CTX *query, DALLOC_CTX *reply);
80 static bool slrpc_fetch_attributes(struct mds_ctx *mds_ctx,
81 const DALLOC_CTX *query, DALLOC_CTX *reply);
82 static bool slrpc_close_query(struct mds_ctx *mds_ctx,
83 const DALLOC_CTX *query, DALLOC_CTX *reply);
85 /************************************************
86 * Misc utility functions
87 ************************************************/
90 * Add requested metadata for a query result element
92 * This could be rewritten to something more sophisticated like
93 * querying metadata from Tracker.
95 * If path or sp is NULL, simply add nil values for all attributes.
97 static bool add_filemeta(struct mds_ctx *mds_ctx,
101 const struct stat_ex *sp)
105 int i, metacount, result;
109 const char *attribute;
111 const char *nfc_path = path;
113 char *nfd_path = NULL;
115 size_t dest_remaining;
118 metacount = dalloc_size(reqinfo);
119 if (metacount == 0 || path == NULL || sp == NULL) {
120 result = dalloc_add_copy(fm_array, &nil, sl_nil_t);
127 meta = dalloc_zero(fm_array, sl_array_t);
132 nfc_len = strlen(nfc_path);
134 * Simple heuristic, strlen by two should give enough room for NFC to
137 nfd_buf_size = nfc_len * 2;
138 nfd_path = talloc_array(meta, char, nfd_buf_size);
139 if (nfd_path == NULL) {
143 dest_remaining = talloc_array_length(dest);
145 nconv = smb_iconv(mds_ctx->ic_nfc_to_nfd,
150 if (nconv == (size_t)-1) {
154 for (i = 0; i < metacount; i++) {
155 attribute = dalloc_get_object(reqinfo, i);
156 if (attribute == NULL) {
159 if (strcmp(attribute, "kMDItemDisplayName") == 0
160 || strcmp(attribute, "kMDItemFSName") == 0) {
161 p = strrchr(nfd_path, '/');
163 result = dalloc_stradd(meta, p + 1);
168 } else if (strcmp(attribute, "kMDItemPath") == 0) {
169 result = dalloc_stradd(meta, nfd_path);
173 } else if (strcmp(attribute, "kMDItemFSSize") == 0) {
174 uint64var = sp->st_ex_size;
175 result = dalloc_add_copy(meta, &uint64var, uint64_t);
179 } else if (strcmp(attribute, "kMDItemFSOwnerUserID") == 0) {
180 uint64var = sp->st_ex_uid;
181 result = dalloc_add_copy(meta, &uint64var, uint64_t);
185 } else if (strcmp(attribute, "kMDItemFSOwnerGroupID") == 0) {
186 uint64var = sp->st_ex_gid;
187 result = dalloc_add_copy(meta, &uint64var, uint64_t);
191 } else if (strcmp(attribute, "kMDItemFSContentChangeDate") == 0) {
192 sl_time.tv_sec = sp->st_ex_mtime.tv_sec;
193 result = dalloc_add_copy(meta, &sl_time, sl_time_t);
198 result = dalloc_add_copy(meta, &nil, sl_nil_t);
205 result = dalloc_add(fm_array, meta, sl_array_t);
212 static int cnid_comp_fn(const void *p1, const void *p2)
214 const uint64_t *cnid1 = p1, *cnid2 = p2;
215 if (*cnid1 == *cnid2) {
218 if (*cnid1 < *cnid2) {
225 * Create a sorted copy of a CNID array
227 static bool sort_cnids(struct sl_query *slq, const DALLOC_CTX *d)
229 uint64_t *cnids = NULL;
233 cnids = talloc_array(slq, uint64_t, dalloc_size(d));
238 for (i = 0; i < dalloc_size(d); i++) {
239 p = dalloc_get_object(d, i);
243 memcpy(&cnids[i], p, sizeof(uint64_t));
245 qsort(cnids, dalloc_size(d), sizeof(uint64_t), cnid_comp_fn);
248 slq->cnids_num = dalloc_size(d);
254 * Allocate result handle used in the async Tracker cursor result
255 * handler for storing results
257 static bool create_result_handle(struct sl_query *slq)
260 struct sl_rslts *query_results;
263 if (slq->query_results) {
264 DEBUG(1, ("unexpected existing result handle\n"));
268 query_results = talloc_zero(slq, struct sl_rslts);
269 if (query_results == NULL) {
274 query_results->cnids = talloc_zero(query_results, sl_cnids_t);
275 if (query_results->cnids == NULL) {
278 query_results->cnids->ca_cnids = dalloc_new(query_results->cnids);
279 if (query_results->cnids->ca_cnids == NULL) {
283 query_results->cnids->ca_unkn1 = 0xadd;
284 if (slq->ctx2 > UINT32_MAX) {
285 DEBUG(1,("64bit ctx2 id too large: 0x%jx", (uintmax_t)slq->ctx2));
288 query_results->cnids->ca_context = (uint32_t)slq->ctx2;
291 query_results->fm_array = dalloc_zero(query_results, sl_array_t);
292 if (query_results->fm_array == NULL) {
296 /* For some reason the list of results always starts with a nil entry */
297 result = dalloc_add_copy(query_results->fm_array, &nil, sl_nil_t);
302 slq->query_results = query_results;
306 static bool add_results(sl_array_t *array, struct sl_query *slq)
314 fm = dalloc_zero(array, sl_filemeta_t);
319 result = dalloc_add_copy(array, &status, uint64_t);
323 result = dalloc_add(array, slq->query_results->cnids, sl_cnids_t);
327 if (slq->query_results->num_results > 0) {
328 result = dalloc_add(fm, slq->query_results->fm_array, sl_array_t);
333 result = dalloc_add(array, fm, sl_filemeta_t);
338 /* This ensure the results get clean up after been sent to the client */
339 talloc_move(array, &slq->query_results);
341 ok = create_result_handle(slq);
343 DEBUG(1, ("couldn't add result handle\n"));
344 slq->state = SLQ_STATE_ERROR;
351 static const struct slrpc_cmd *slrpc_cmd_by_name(const char *rpccmd)
354 static const struct slrpc_cmd cmds[] = {
355 { "fetchPropertiesForContext:", slrpc_fetch_properties},
356 { "openQueryWithParams:forContext:", slrpc_open_query},
357 { "fetchQueryResultsForContext:", slrpc_fetch_query_results},
358 { "storeAttributes:forOIDArray:context:", slrpc_store_attributes},
359 { "fetchAttributeNamesForOIDArray:context:", slrpc_fetch_attributenames},
360 { "fetchAttributes:forOIDArray:context:", slrpc_fetch_attributes},
361 { "fetchAllAttributes:forOIDArray:context:", slrpc_fetch_attributes},
362 { "closeQueryForContext:", slrpc_close_query},
365 for (i = 0; i < ARRAY_SIZE(cmds); i++) {
368 cmp = strcmp(cmds[i].name, rpccmd);
378 * Search the list of active queries given their context ids
380 static struct sl_query *slq_for_ctx(struct mds_ctx *mds_ctx,
381 uint64_t ctx1, uint64_t ctx2)
385 for (q = mds_ctx->query_list; q; q = q->next) {
386 if ((q->ctx1 == ctx1) && (q->ctx2 == ctx2)) {
394 static int slq_destructor_cb(struct sl_query *slq)
396 SLQ_DEBUG(10, slq, "destroying");
398 /* Free all entries before freeing the slq handle! */
399 TALLOC_FREE(slq->entries_ctx);
400 TALLOC_FREE(slq->te);
402 if (slq->mds_ctx != NULL) {
403 DLIST_REMOVE(slq->mds_ctx->query_list, slq);
407 TALLOC_FREE(slq->backend_private);
413 * Remove talloc_refcounted entry from mapping db
415 * Multiple queries (via the slq handle) may reference a
416 * sl_inode_path_map entry, when the last reference goes away as the
417 * queries are closed and this gets called to remove the entry from
420 static int ino_path_map_destr_cb(struct sl_inode_path_map *entry)
425 key = make_tdb_data((uint8_t *)&entry->ino, sizeof(entry->ino));
427 status = dbwrap_delete(entry->mds_ctx->ino_path_map, key);
428 if (!NT_STATUS_IS_OK(status)) {
429 DEBUG(1, ("Failed to delete record: %s\n", nt_errstr(status)));
433 DBG_DEBUG("deleted [0x%"PRIx64"] [%s]\n", entry->ino, entry->path);
438 * Add result to inode->path mapping dbwrap rbt db
440 * This is necessary as a CNID db substitute, ie we need a way to
441 * simulate unique, constant numerical identifiers for paths with an
442 * API that supports mapping from id to path.
444 * Entries are talloc'ed of the query, using talloc_reference() if
445 * multiple queries returned the same result. That way we can cleanup
446 * entries by calling talloc_free() on the query slq handles.
449 static bool inode_map_add(struct sl_query *slq,
455 struct sl_inode_path_map *entry;
459 key = make_tdb_data((uint8_t *)&ino, sizeof(ino));
460 status = dbwrap_fetch(slq->mds_ctx->ino_path_map, slq, key, &value);
462 if (NT_STATUS_IS_OK(status)) {
464 * We have one db, so when different parallel queries
465 * return the same file, we have to refcount entries
469 if (value.dsize != sizeof(void *)) {
470 DEBUG(1, ("invalide dsize\n"));
473 memcpy(&p, value.dptr, sizeof(p));
474 entry = talloc_get_type_abort(p, struct sl_inode_path_map);
476 DEBUG(10, ("map: %s\n", entry->path));
478 entry = talloc_reference(slq->entries_ctx, entry);
480 DEBUG(1, ("talloc_reference failed\n"));
486 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
487 DEBUG(1, ("dbwrap_fetch failed %s\n", nt_errstr(status)));
491 entry = talloc_zero(slq->entries_ctx, struct sl_inode_path_map);
493 DEBUG(1, ("talloc failed\n"));
498 entry->mds_ctx = slq->mds_ctx;
500 entry->path = talloc_strdup(entry, path);
501 if (entry->path == NULL) {
502 DEBUG(1, ("talloc failed\n"));
507 status = dbwrap_store(slq->mds_ctx->ino_path_map, key,
508 make_tdb_data((void *)&entry, sizeof(void *)), 0);
509 if (!NT_STATUS_IS_OK(status)) {
510 DEBUG(1, ("Failed to store record: %s\n", nt_errstr(status)));
515 talloc_set_destructor(entry, ino_path_map_destr_cb);
520 bool mds_add_result(struct sl_query *slq, const char *path)
522 struct smb_filename *smb_fname = NULL;
531 * We're in a tevent callback which means in the case of
532 * running as external RPC service we're running as root and
535 if (!become_authenticated_pipe_user(slq->mds_ctx->pipe_session_info)) {
536 DBG_ERR("can't become authenticated user: %d\n",
538 smb_panic("can't become authenticated user");
541 if (geteuid() != slq->mds_ctx->uid) {
542 DBG_ERR("uid mismatch: %d/%d\n", geteuid(), slq->mds_ctx->uid);
543 smb_panic("uid mismatch");
547 * We've changed identity to the authenticated pipe user, so
548 * any function exit below must ensure we switch back
551 status = synthetic_pathref(talloc_tos(),
552 slq->mds_ctx->conn->cwd_fsp,
559 if (!NT_STATUS_IS_OK(status)) {
560 DBG_DEBUG("synthetic_pathref [%s]: %s\n",
561 smb_fname_str_dbg(smb_fname),
563 unbecome_authenticated_pipe_user();
567 status = smbd_check_access_rights_fsp(slq->mds_ctx->conn->cwd_fsp,
571 if (!NT_STATUS_IS_OK(status)) {
572 unbecome_authenticated_pipe_user();
573 TALLOC_FREE(smb_fname);
577 /* This is needed to fetch the itime from the DOS attribute blob */
578 status = SMB_VFS_FGET_DOS_ATTRIBUTES(slq->mds_ctx->conn,
581 if (!NT_STATUS_IS_OK(status)) {
582 /* Ignore the error, likely no DOS attr xattr */
583 DBG_DEBUG("SMB_VFS_FGET_DOS_ATTRIBUTES [%s]: %s\n",
584 smb_fname_str_dbg(smb_fname),
588 unbecome_authenticated_pipe_user();
590 smb_fname->st = smb_fname->fsp->fsp_name->st;
592 /* Done with smb_fname now. */
593 TALLOC_FREE(smb_fname);
594 ino64 = SMB_VFS_FS_FILE_ID(slq->mds_ctx->conn, &sb);
600 * Check whether the found element is in the requested
601 * set of IDs. Note that we're faking CNIDs by using
602 * filesystem inode numbers here
604 found = bsearch(&ino64,
615 * Add inode number and filemeta to result set, this is what
616 * we return as part of the result set of a query
618 result = dalloc_add_copy(slq->query_results->cnids->ca_cnids,
622 DBG_ERR("dalloc error\n");
623 slq->state = SLQ_STATE_ERROR;
626 ok = add_filemeta(slq->mds_ctx,
628 slq->query_results->fm_array,
632 DBG_ERR("add_filemeta error\n");
633 slq->state = SLQ_STATE_ERROR;
637 ok = inode_map_add(slq, ino64, path, &sb);
639 DEBUG(1, ("inode_map_add error\n"));
640 slq->state = SLQ_STATE_ERROR;
644 slq->query_results->num_results++;
648 /***********************************************************
649 * Spotlight RPC functions
650 ***********************************************************/
652 static bool slrpc_fetch_properties(struct mds_ctx *mds_ctx,
653 const DALLOC_CTX *query, DALLOC_CTX *reply)
663 dict = dalloc_zero(reply, sl_dict_t);
668 /* kMDSStoreHasPersistentUUID = false */
669 result = dalloc_stradd(dict, "kMDSStoreHasPersistentUUID");
674 result = dalloc_add_copy(dict, &b, sl_bool_t);
679 /* kMDSStoreIsBackup = false */
680 result = dalloc_stradd(dict, "kMDSStoreIsBackup");
685 result = dalloc_add_copy(dict, &b, sl_bool_t);
690 /* kMDSStoreUUID = uuid */
691 result = dalloc_stradd(dict, "kMDSStoreUUID");
695 memcpy(uuid.sl_uuid, "fakeuuidfakeuuid", sizeof(uuid.sl_uuid));
696 result = dalloc_add_copy(dict, &uuid, sl_uuid_t);
701 /* kMDSStoreSupportsVolFS = true */
702 result = dalloc_stradd(dict, "kMDSStoreSupportsVolFS");
707 result = dalloc_add_copy(dict, &b, sl_bool_t);
712 /* kMDSVolumeUUID = uuid */
713 result = dalloc_stradd(dict, "kMDSVolumeUUID");
717 memcpy(uuid.sl_uuid, "fakeuuidfakeuuid", sizeof(uuid.sl_uuid));
718 result = dalloc_add_copy(dict, &uuid, sl_uuid_t);
723 /* kMDSDiskStoreSpindleNumber = 1 (fake) */
724 result = dalloc_stradd(dict, "kMDSDiskStoreSpindleNumber");
729 result = dalloc_add_copy(dict, &u, uint64_t);
734 /* kMDSDiskStorePolicy = 3 (whatever that means, taken from OS X) */
735 result = dalloc_stradd(dict, "kMDSDiskStorePolicy");
740 result = dalloc_add_copy(dict, &u, uint64_t);
745 /* kMDSStoreMetaScopes array */
746 result = dalloc_stradd(dict, "kMDSStoreMetaScopes");
750 array = dalloc_zero(dict, sl_array_t);
754 result = dalloc_stradd(array, "kMDQueryScopeComputer");
758 result = dalloc_stradd(array, "kMDQueryScopeAllIndexed");
762 result = dalloc_stradd(array, "kMDQueryScopeComputerIndexed");
766 result = dalloc_add(dict, array, sl_array_t);
771 /* kMDSStoreDevice = 0x1000003 (whatever that means, taken from OS X) */
772 result = dalloc_stradd(dict, "kMDSStoreDevice");
777 result = dalloc_add_copy(dict, &u, uint64_t);
782 /* kMDSStoreSupportsTCC = true (whatever that means, taken from OS X) */
783 result = dalloc_stradd(dict, "kMDSStoreSupportsTCC");
788 result = dalloc_add_copy(dict, &b, sl_bool_t);
793 /* kMDSStorePathScopes = ["/"] (whatever that means, taken from OS X) */
794 result = dalloc_stradd(dict, "kMDSStorePathScopes");
798 array = dalloc_zero(dict, sl_array_t);
802 s = talloc_strdup(dict, "/");
806 talloc_set_name(s, "smb_ucs2_t *");
807 result = dalloc_add(array, s, smb_ucs2_t *);
811 result = dalloc_add(dict, array, sl_array_t);
816 result = dalloc_add(reply, dict, sl_dict_t);
824 static void slq_close_timer(struct tevent_context *ev,
825 struct tevent_timer *te,
826 struct timeval current_time,
829 struct sl_query *slq = talloc_get_type_abort(
830 private_data, struct sl_query);
831 struct mds_ctx *mds_ctx = slq->mds_ctx;
833 SLQ_DEBUG(10, slq, "expired");
837 if (CHECK_DEBUGLVL(10)) {
838 for (slq = mds_ctx->query_list; slq != NULL; slq = slq->next) {
839 SLQ_DEBUG(10, slq, "pending");
845 * Begin a search query
847 static bool slrpc_open_query(struct mds_ctx *mds_ctx,
848 const DALLOC_CTX *query, DALLOC_CTX *reply)
854 sl_array_t *array, *path_scope;
856 struct sl_query *slq = NULL;
858 const char *querystring = NULL;
859 size_t querystring_len;
861 size_t dest_remaining;
865 array = dalloc_zero(reply, sl_array_t);
870 /* Allocate and initialize query object */
871 slq = talloc_zero(mds_ctx, struct sl_query);
875 slq->entries_ctx = talloc_named_const(slq, 0, "struct sl_query.entries_ctx");
876 if (slq->entries_ctx == NULL) {
880 talloc_set_destructor(slq, slq_destructor_cb);
881 slq->state = SLQ_STATE_NEW;
882 slq->mds_ctx = mds_ctx;
884 slq->last_used = timeval_current();
885 slq->start_time = slq->last_used;
886 slq->expire_time = timeval_add(&slq->last_used, MAX_SL_RUNTIME, 0);
887 slq->te = tevent_add_timer(global_event_context(), slq,
888 slq->expire_time, slq_close_timer, slq);
889 if (slq->te == NULL) {
890 DEBUG(1, ("tevent_add_timer failed\n"));
894 querystring = dalloc_value_for_key(query, "DALLOC_CTX", 0,
898 if (querystring == NULL) {
899 DEBUG(1, ("missing kMDQueryString\n"));
903 querystring_len = talloc_array_length(querystring);
905 slq->query_string = talloc_array(slq, char, querystring_len);
906 if (slq->query_string == NULL) {
907 DEBUG(1, ("out of memory\n"));
910 dest = slq->query_string;
911 dest_remaining = talloc_array_length(dest);
913 nconv = smb_iconv(mds_ctx->ic_nfd_to_nfc,
918 if (nconv == (size_t)-1) {
919 DBG_ERR("smb_iconv failed for: %s\n", querystring);
923 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
925 if (uint64p == NULL) {
928 slq->ctx1 = *uint64p;
929 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
931 if (uint64p == NULL) {
934 slq->ctx2 = *uint64p;
936 path_scope = dalloc_value_for_key(query, "DALLOC_CTX", 0,
940 if (path_scope == NULL) {
941 DBG_ERR("missing kMDScopeArray\n");
945 scope = dalloc_get(path_scope, "char *", 0);
947 scope = dalloc_get(path_scope,
952 DBG_ERR("Failed to parse kMDScopeArray\n");
956 slq->path_scope = talloc_strdup(slq, scope);
957 if (slq->path_scope == NULL) {
961 reqinfo = dalloc_value_for_key(query, "DALLOC_CTX", 0,
965 if (reqinfo == NULL) {
966 DBG_ERR("missing kMDAttributeArray\n");
970 slq->reqinfo = talloc_steal(slq, reqinfo);
971 DEBUG(10, ("requested attributes: %s", dalloc_dump(reqinfo, 0)));
973 cnids = dalloc_value_for_key(query, "DALLOC_CTX", 0,
978 ok = sort_cnids(slq, cnids->ca_cnids);
984 ok = create_result_handle(slq);
986 DEBUG(1, ("create_result_handle error\n"));
987 slq->state = SLQ_STATE_ERROR;
991 SLQ_DEBUG(10, slq, "new");
993 DLIST_ADD(mds_ctx->query_list, slq);
995 ok = mds_ctx->backend->search_start(slq);
997 DBG_ERR("backend search_start failed\n");
1002 result = dalloc_add_copy(array, &sl_result, uint64_t);
1006 result = dalloc_add(reply, array, sl_array_t);
1013 sl_result = UINT64_MAX;
1015 result = dalloc_add_copy(array, &sl_result, uint64_t);
1019 result = dalloc_add(reply, array, sl_array_t);
1027 * Fetch results of a query
1029 static bool slrpc_fetch_query_results(struct mds_ctx *mds_ctx,
1030 const DALLOC_CTX *query,
1034 struct sl_query *slq = NULL;
1035 uint64_t *uint64p, ctx1, ctx2;
1040 array = dalloc_zero(reply, sl_array_t);
1041 if (array == NULL) {
1045 /* Get query for context */
1046 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1048 if (uint64p == NULL) {
1053 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1055 if (uint64p == NULL) {
1060 slq = slq_for_ctx(mds_ctx, ctx1, ctx2);
1062 DEBUG(1, ("bad context: [0x%jx,0x%jx]\n",
1063 (uintmax_t)ctx1, (uintmax_t)ctx2));
1067 TALLOC_FREE(slq->te);
1068 slq->last_used = timeval_current();
1069 slq->expire_time = timeval_add(&slq->last_used, MAX_SL_RUNTIME, 0);
1070 slq->te = tevent_add_timer(global_event_context(), slq,
1071 slq->expire_time, slq_close_timer, slq);
1072 if (slq->te == NULL) {
1073 DEBUG(1, ("tevent_add_timer failed\n"));
1077 SLQ_DEBUG(10, slq, "fetch");
1079 switch (slq->state) {
1080 case SLQ_STATE_RUNNING:
1081 case SLQ_STATE_RESULTS:
1082 case SLQ_STATE_FULL:
1083 case SLQ_STATE_DONE:
1084 ok = add_results(array, slq);
1086 DEBUG(1, ("error adding results\n"));
1089 if (slq->state == SLQ_STATE_FULL) {
1090 slq->state = SLQ_STATE_RESULTS;
1091 slq->mds_ctx->backend->search_cont(slq);
1095 case SLQ_STATE_ERROR:
1096 DEBUG(1, ("query in error state\n"));
1100 DEBUG(1, ("unexpected query state %d\n", slq->state));
1104 result = dalloc_add(reply, array, sl_array_t);
1111 status = UINT64_MAX;
1113 result = dalloc_add_copy(array, &status, uint64_t);
1117 result = dalloc_add(reply, array, sl_array_t);
1125 * Store metadata attributes for a CNID
1127 static bool slrpc_store_attributes(struct mds_ctx *mds_ctx,
1128 const DALLOC_CTX *query, DALLOC_CTX *reply)
1134 array = dalloc_zero(reply, sl_array_t);
1135 if (array == NULL) {
1140 * FIXME: not implemented. Used by the client for eg setting
1141 * the modification date of the shared directory which clients
1142 * poll indicating changes on the share and cause the client
1147 result = dalloc_add_copy(array, &sl_result, uint64_t);
1151 result = dalloc_add(reply, array, sl_array_t);
1160 * Fetch supported metadata attributes for a CNID
1162 static bool slrpc_fetch_attributenames(struct mds_ctx *mds_ctx,
1163 const DALLOC_CTX *query,
1170 sl_cnids_t *replycnids;
1171 sl_array_t *mdattrs;
1172 sl_filemeta_t *fmeta;
1176 cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 1);
1177 if (cnids == NULL) {
1181 p = dalloc_get_object(cnids->ca_cnids, 0);
1185 memcpy(&id, p, sizeof(uint64_t));
1188 array = dalloc_zero(reply, sl_array_t);
1189 if (array == NULL) {
1193 result = dalloc_add(reply, array, sl_array_t);
1198 /* Return result value 0 */
1200 result = dalloc_add_copy(array, &sl_result, uint64_t);
1205 /* Return CNID array */
1206 replycnids = talloc_zero(reply, sl_cnids_t);
1207 if (replycnids == NULL) {
1211 replycnids->ca_cnids = dalloc_new(cnids);
1212 if (replycnids->ca_cnids == NULL) {
1216 replycnids->ca_unkn1 = 0xfec;
1217 replycnids->ca_context = cnids->ca_context;
1218 result = dalloc_add_copy(replycnids->ca_cnids, &id, uint64_t);
1222 result = dalloc_add(array, replycnids, sl_cnids_t);
1228 * FIXME: this should return the real attributes from all
1229 * known metadata sources (Tracker and filesystem)
1231 mdattrs = dalloc_zero(reply, sl_array_t);
1232 if (mdattrs == NULL) {
1236 result = dalloc_stradd(mdattrs, "kMDItemFSName");
1240 result = dalloc_stradd(mdattrs, "kMDItemDisplayName");
1244 result = dalloc_stradd(mdattrs, "kMDItemFSSize");
1248 result = dalloc_stradd(mdattrs, "kMDItemFSOwnerUserID");
1252 result = dalloc_stradd(mdattrs, "kMDItemFSOwnerGroupID");
1256 result = dalloc_stradd(mdattrs, "kMDItemFSContentChangeDate");
1261 fmeta = dalloc_zero(reply, sl_filemeta_t);
1262 if (fmeta == NULL) {
1265 result = dalloc_add(fmeta, mdattrs, sl_array_t);
1269 result = dalloc_add(array, fmeta, sl_filemeta_t);
1278 * Fetch metadata attribute values for a CNID
1280 static bool slrpc_fetch_attributes(struct mds_ctx *mds_ctx,
1281 const DALLOC_CTX *query, DALLOC_CTX *reply)
1287 sl_cnids_t *replycnids;
1288 sl_array_t *reqinfo;
1292 sl_array_t *fm_array;
1295 struct smb_filename *smb_fname = NULL;
1296 struct stat_ex *sp = NULL;
1297 struct sl_inode_path_map *elem = NULL;
1299 TDB_DATA val = tdb_null;
1302 array = dalloc_zero(reply, sl_array_t);
1303 if (array == NULL) {
1306 replycnids = talloc_zero(reply, sl_cnids_t);
1307 if (replycnids == NULL) {
1310 replycnids->ca_cnids = dalloc_new(replycnids);
1311 if (replycnids->ca_cnids == NULL) {
1314 fm = dalloc_zero(array, sl_filemeta_t);
1318 fm_array = dalloc_zero(fm, sl_array_t);
1319 if (fm_array == NULL) {
1322 /* For some reason the list of results always starts with a nil entry */
1323 result = dalloc_add_copy(fm_array, &nil, sl_nil_t);
1328 reqinfo = dalloc_get(query, "DALLOC_CTX", 0, "sl_array_t", 1);
1329 if (reqinfo == NULL) {
1333 cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 2);
1334 if (cnids == NULL) {
1337 p = dalloc_get_object(cnids->ca_cnids, 0);
1341 memcpy(&ino, p, sizeof(uint64_t));
1343 replycnids->ca_unkn1 = 0xfec;
1344 replycnids->ca_context = cnids->ca_context;
1345 result = dalloc_add_copy(replycnids->ca_cnids, &ino, uint64_t);
1350 status = dbwrap_fetch(mds_ctx->ino_path_map, reply,
1351 make_tdb_data((void*)&ino, sizeof(uint64_t)),
1353 if (NT_STATUS_IS_OK(status)) {
1354 if (val.dsize != sizeof(p)) {
1355 DBG_ERR("invalid record pointer size: %zd\n", val.dsize);
1356 TALLOC_FREE(val.dptr);
1360 memcpy(&p, val.dptr, sizeof(p));
1361 elem = talloc_get_type_abort(p, struct sl_inode_path_map);
1367 ok = add_filemeta(mds_ctx, reqinfo, fm_array, path, sp);
1373 result = dalloc_add_copy(array, &sl_result, uint64_t);
1377 result = dalloc_add(array, replycnids, sl_cnids_t);
1381 result = dalloc_add(fm, fm_array, sl_array_t);
1385 result = dalloc_add(array, fm, sl_filemeta_t);
1389 result = dalloc_add(reply, array, sl_array_t);
1394 TALLOC_FREE(smb_fname);
1399 TALLOC_FREE(smb_fname);
1400 sl_result = UINT64_MAX;
1401 result = dalloc_add_copy(array, &sl_result, uint64_t);
1405 result = dalloc_add(reply, array, sl_array_t);
1416 static bool slrpc_close_query(struct mds_ctx *mds_ctx,
1417 const DALLOC_CTX *query, DALLOC_CTX *reply)
1419 struct sl_query *slq = NULL;
1420 uint64_t *uint64p, ctx1, ctx2;
1425 array = dalloc_zero(reply, sl_array_t);
1426 if (array == NULL) {
1431 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1433 if (uint64p == NULL) {
1438 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1440 if (uint64p == NULL) {
1445 /* Get query for context and free it */
1446 slq = slq_for_ctx(mds_ctx, ctx1, ctx2);
1448 DEBUG(1, ("bad context: [0x%jx,0x%jx]\n",
1449 (uintmax_t)ctx1, (uintmax_t)ctx2));
1453 SLQ_DEBUG(10, slq, "close");
1457 sl_res = UINT64_MAX;
1458 result = dalloc_add_copy(array, &sl_res, uint64_t);
1462 result = dalloc_add(reply, array, sl_array_t);
1469 static struct mdssvc_ctx *mdssvc_init(struct tevent_context *ev)
1473 if (mdssvc_ctx != NULL) {
1477 mdssvc_ctx = talloc_zero(ev, struct mdssvc_ctx);
1478 if (mdssvc_ctx == NULL) {
1482 mdssvc_ctx->ev_ctx = ev;
1484 ok = mdsscv_backend_noindex.init(mdssvc_ctx);
1486 DBG_ERR("backend init failed\n");
1487 TALLOC_FREE(mdssvc_ctx);
1491 #ifdef HAVE_SPOTLIGHT_BACKEND_ES
1492 ok = mdsscv_backend_es.init(mdssvc_ctx);
1494 DBG_ERR("backend init failed\n");
1495 TALLOC_FREE(mdssvc_ctx);
1500 #ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
1501 ok = mdsscv_backend_tracker.init(mdssvc_ctx);
1503 DBG_ERR("backend init failed\n");
1504 TALLOC_FREE(mdssvc_ctx);
1513 * Init callbacks at startup
1515 * This gets typically called in the main parent smbd which means we can't
1516 * initialize our global state here.
1518 bool mds_init(struct messaging_context *msg_ctx)
1523 bool mds_shutdown(void)
1527 if (mdssvc_ctx == NULL) {
1531 ok = mdsscv_backend_noindex.shutdown(mdssvc_ctx);
1536 #ifdef HAVE_SPOTLIGHT_BACKEND_ES
1537 ok = mdsscv_backend_es.shutdown(mdssvc_ctx);
1543 #ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
1544 ok = mdsscv_backend_tracker.shutdown(mdssvc_ctx);
1552 TALLOC_FREE(mdssvc_ctx);
1557 * Tear down connections and free all resources
1559 static int mds_ctx_destructor_cb(struct mds_ctx *mds_ctx)
1562 * We need to free query_list before ino_path_map
1564 while (mds_ctx->query_list != NULL) {
1566 * slq destructor removes element from list.
1567 * Don't use TALLOC_FREE()!
1569 talloc_free(mds_ctx->query_list);
1571 TALLOC_FREE(mds_ctx->ino_path_map);
1573 if (mds_ctx->conn != NULL) {
1574 SMB_VFS_DISCONNECT(mds_ctx->conn);
1575 conn_free(mds_ctx->conn);
1578 ZERO_STRUCTP(mds_ctx);
1584 * Initialise a context per RPC bind
1586 * This ends up being called for every tcon, because the client does a
1587 * RPC bind for every tcon, so this is acually a per tcon context.
1589 NTSTATUS mds_init_ctx(TALLOC_CTX *mem_ctx,
1590 struct tevent_context *ev,
1591 struct messaging_context *msg_ctx,
1592 struct auth_session_info *session_info,
1594 const char *sharename,
1596 struct mds_ctx **_mds_ctx)
1598 const struct loadparm_substitution *lp_sub =
1599 loadparm_s3_global_substitution();
1600 struct smb_filename conn_basedir;
1601 struct mds_ctx *mds_ctx;
1605 smb_iconv_t iconv_hnd = (smb_iconv_t)-1;
1608 if (!lp_spotlight(snum)) {
1609 return NT_STATUS_WRONG_VOLUME;
1612 mds_ctx = talloc_zero(mem_ctx, struct mds_ctx);
1613 if (mds_ctx == NULL) {
1614 return NT_STATUS_NO_MEMORY;
1616 talloc_set_destructor(mds_ctx, mds_ctx_destructor_cb);
1618 mds_ctx->mdssvc_ctx = mdssvc_init(ev);
1619 if (mds_ctx->mdssvc_ctx == NULL) {
1620 return NT_STATUS_NO_MEMORY;
1623 backend = lp_spotlight_backend(snum);
1625 case SPOTLIGHT_BACKEND_NOINDEX:
1626 mds_ctx->backend = &mdsscv_backend_noindex;
1629 #ifdef HAVE_SPOTLIGHT_BACKEND_ES
1630 case SPOTLIGHT_BACKEND_ES:
1631 mds_ctx->backend = &mdsscv_backend_es;
1635 #ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
1636 case SPOTLIGHT_BACKEND_TRACKER:
1637 mds_ctx->backend = &mdsscv_backend_tracker;
1641 DBG_ERR("Unknown backend %d\n", backend);
1642 TALLOC_FREE(mdssvc_ctx);
1643 status = NT_STATUS_INTERNAL_ERROR;
1647 iconv_hnd = smb_iconv_open_ex(mds_ctx,
1651 if (iconv_hnd == (smb_iconv_t)-1) {
1652 status = NT_STATUS_INTERNAL_ERROR;
1655 mds_ctx->ic_nfc_to_nfd = iconv_hnd;
1657 iconv_hnd = smb_iconv_open_ex(mds_ctx,
1661 if (iconv_hnd == (smb_iconv_t)-1) {
1662 status = NT_STATUS_INTERNAL_ERROR;
1665 mds_ctx->ic_nfd_to_nfc = iconv_hnd;
1667 mds_ctx->sharename = talloc_strdup(mds_ctx, sharename);
1668 if (mds_ctx->sharename == NULL) {
1669 status = NT_STATUS_NO_MEMORY;
1673 mds_ctx->spath = talloc_strdup(mds_ctx, path);
1674 if (mds_ctx->spath == NULL) {
1675 status = NT_STATUS_NO_MEMORY;
1679 mds_ctx->snum = snum;
1680 mds_ctx->pipe_session_info = session_info;
1682 if (session_info->security_token->num_sids < 1) {
1683 status = NT_STATUS_BAD_LOGON_SESSION_STATE;
1686 sid_copy(&mds_ctx->sid, &session_info->security_token->sids[0]);
1687 mds_ctx->uid = session_info->unix_token->uid;
1689 mds_ctx->ino_path_map = db_open_rbt(mds_ctx);
1690 if (mds_ctx->ino_path_map == NULL) {
1691 DEBUG(1,("open inode map db failed\n"));
1692 status = NT_STATUS_INTERNAL_ERROR;
1696 status = create_conn_struct_cwd(mds_ctx,
1701 lp_path(talloc_tos(), lp_sub, snum),
1703 if (!NT_STATUS_IS_OK(status)) {
1704 DBG_ERR("failed to create conn for vfs: %s\n",
1709 conn_basedir = (struct smb_filename) {
1710 .base_name = mds_ctx->conn->connectpath,
1713 ret = vfs_ChDir(mds_ctx->conn, &conn_basedir);
1715 DBG_ERR("vfs_ChDir [%s] failed: %s\n",
1716 conn_basedir.base_name, strerror(errno));
1717 status = map_nt_error_from_unix(errno);
1721 ok = mds_ctx->backend->connect(mds_ctx);
1723 DBG_ERR("backend connect failed\n");
1724 status = NT_STATUS_CONNECTION_RESET;
1728 *_mds_ctx = mds_ctx;
1729 return NT_STATUS_OK;
1732 if (mds_ctx->ic_nfc_to_nfd != NULL) {
1733 smb_iconv_close(mds_ctx->ic_nfc_to_nfd);
1735 if (mds_ctx->ic_nfd_to_nfc != NULL) {
1736 smb_iconv_close(mds_ctx->ic_nfd_to_nfc);
1739 TALLOC_FREE(mds_ctx);
1744 * Dispatch a Spotlight RPC command
1746 bool mds_dispatch(struct mds_ctx *mds_ctx,
1747 struct mdssvc_blob *request_blob,
1748 struct mdssvc_blob *response_blob)
1753 DALLOC_CTX *query = NULL;
1754 DALLOC_CTX *reply = NULL;
1756 const struct slrpc_cmd *slcmd;
1757 const struct smb_filename conn_basedir = {
1758 .base_name = mds_ctx->conn->connectpath,
1761 if (CHECK_DEBUGLVL(10)) {
1762 const struct sl_query *slq;
1764 for (slq = mds_ctx->query_list; slq != NULL; slq = slq->next) {
1765 SLQ_DEBUG(10, slq, "pending");
1769 response_blob->length = 0;
1771 DEBUG(10, ("share path: %s\n", mds_ctx->spath));
1773 query = dalloc_new(mds_ctx);
1774 if (query == NULL) {
1778 reply = dalloc_new(mds_ctx);
1779 if (reply == NULL) {
1784 ok = sl_unpack(query, (char *)request_blob->spotlight_blob,
1785 request_blob->length);
1787 DEBUG(1, ("error unpacking Spotlight RPC blob\n"));
1791 DEBUG(5, ("%s", dalloc_dump(query, 0)));
1793 rpccmd = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1795 if (rpccmd == NULL) {
1796 DEBUG(1, ("missing primary Spotlight RPC command\n"));
1801 DEBUG(10, ("Spotlight RPC cmd: %s\n", rpccmd));
1803 slcmd = slrpc_cmd_by_name(rpccmd);
1804 if (slcmd == NULL) {
1805 DEBUG(1, ("unsupported primary Spotlight RPC command %s\n",
1811 ret = vfs_ChDir(mds_ctx->conn, &conn_basedir);
1813 DBG_ERR("vfs_ChDir [%s] failed: %s\n",
1814 conn_basedir.base_name, strerror(errno));
1819 ok = slcmd->function(mds_ctx, query, reply);
1824 DBG_DEBUG("%s", dalloc_dump(reply, 0));
1826 len = sl_pack(reply,
1827 (char *)response_blob->spotlight_blob,
1828 response_blob->size);
1830 DBG_ERR("error packing Spotlight RPC reply\n");
1834 response_blob->length = len;