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 "librpc/gen_ndr/auth.h"
23 #include "dbwrap/dbwrap.h"
24 #include "lib/util/dlinklist.h"
25 #include "lib/util/util_tdb.h"
26 #include "lib/util/time_basic.h"
27 #include "lib/dbwrap/dbwrap_rbt.h"
28 #include "libcli/security/dom_sid.h"
30 #include "rpc_server/mdssvc/sparql_parser.tab.h"
33 #define DBGC_CLASS DBGC_RPC_SRV
35 #define SLQ_DEBUG(lvl, _slq, state) do { if (CHECK_DEBUGLVL(lvl)) { \
36 const struct sl_query *__slq = _slq; \
37 struct timeval_buf start_buf; \
39 struct timeval_buf last_used_buf; \
40 const char *last_used; \
41 struct timeval_buf expire_buf; \
43 start = timeval_str_buf(&__slq->start_time, false, \
45 last_used = timeval_str_buf(&__slq->last_used, false, \
46 true, &last_used_buf); \
47 expire = timeval_str_buf(&__slq->expire_time, false, \
49 DEBUG(lvl,("%s slq[0x%jx,0x%jx], start: %s, last_used: %s, " \
50 "expires: %s, query: '%s'\n", state, \
51 (uintmax_t)__slq->ctx1, (uintmax_t)__slq->ctx2, \
52 start, last_used, expire, __slq->query_string)); \
57 bool (*function)(struct mds_ctx *mds_ctx,
58 const DALLOC_CTX *query,
62 struct slq_destroy_state {
63 struct tevent_context *ev;
68 * If these functions return an error, they hit something like a non
69 * recoverable talloc error. Most errors are dealt with by returning
70 * an errror code in the Spotlight RPC reply.
72 static bool slrpc_fetch_properties(struct mds_ctx *mds_ctx,
73 const DALLOC_CTX *query, DALLOC_CTX *reply);
74 static bool slrpc_open_query(struct mds_ctx *mds_ctx,
75 const DALLOC_CTX *query, DALLOC_CTX *reply);
76 static bool slrpc_fetch_query_results(struct mds_ctx *mds_ctx,
77 const DALLOC_CTX *query, DALLOC_CTX *reply);
78 static bool slrpc_store_attributes(struct mds_ctx *mds_ctx,
79 const DALLOC_CTX *query, DALLOC_CTX *reply);
80 static bool slrpc_fetch_attributenames(struct mds_ctx *mds_ctx,
81 const DALLOC_CTX *query, DALLOC_CTX *reply);
82 static bool slrpc_fetch_attributes(struct mds_ctx *mds_ctx,
83 const DALLOC_CTX *query, DALLOC_CTX *reply);
84 static bool slrpc_close_query(struct mds_ctx *mds_ctx,
85 const DALLOC_CTX *query, DALLOC_CTX *reply);
87 static struct tevent_req *slq_destroy_send(TALLOC_CTX *mem_ctx,
88 struct tevent_context *ev,
89 struct sl_query **slq)
91 struct tevent_req *req;
92 struct slq_destroy_state *state;
94 req = tevent_req_create(mem_ctx, &state,
95 struct slq_destroy_state);
99 state->slq = talloc_move(state, slq);
100 tevent_req_done(req);
102 return tevent_req_post(req, ev);
105 static void slq_destroy_recv(struct tevent_req *req)
107 tevent_req_received(req);
110 /************************************************
111 * Misc utility functions
112 ************************************************/
114 static char *tab_level(TALLOC_CTX *mem_ctx, int level)
117 char *string = talloc_array(mem_ctx, char, level + 1);
119 for (i = 0; i < level; i++) {
127 char *mds_dalloc_dump(DALLOC_CTX *dd, int nestinglevel)
135 char datestring[256];
137 char *logstring, *nested_logstring;
138 char *tab_string1, *tab_string2;
144 tab_string1 = tab_level(dd, nestinglevel);
145 if (tab_string1 == NULL) {
148 tab_string2 = tab_level(dd, nestinglevel + 1);
149 if (tab_string2 == NULL) {
153 logstring = talloc_asprintf(dd,
158 if (logstring == NULL) {
162 for (n = 0; n < dalloc_size(dd); n++) {
163 type = dalloc_get_name(dd, n);
167 p = dalloc_get_object(dd, n);
171 if (strcmp(type, "DALLOC_CTX") == 0
172 || strcmp(type, "sl_array_t") == 0
173 || strcmp(type, "sl_filemeta_t") == 0
174 || strcmp(type, "sl_dict_t") == 0) {
175 nested_logstring = mds_dalloc_dump(p, nestinglevel + 1);
176 if (nested_logstring == NULL) {
179 logstring = talloc_strdup_append(logstring,
181 } else if (strcmp(type, "uint64_t") == 0) {
182 memcpy(&i, p, sizeof(uint64_t));
183 logstring = talloc_asprintf_append(
185 "%suint64_t: 0x%04jx\n",
186 tab_string2, (uintmax_t)i);
187 } else if (strcmp(type, "char *") == 0) {
188 logstring = talloc_asprintf_append(
193 } else if (strcmp(type, "smb_ucs2_t *") == 0) {
194 ok = convert_string_talloc(talloc_tos(),
204 logstring = talloc_asprintf_append(
206 "%sUTF16-string: %s\n",
209 TALLOC_FREE(utf8string);
210 } else if (strcmp(type, "sl_bool_t") == 0) {
211 memcpy(&bl, p, sizeof(sl_bool_t));
212 logstring = talloc_asprintf_append(
216 bl ? "true" : "false");
217 } else if (strcmp(type, "sl_nil_t") == 0) {
218 logstring = talloc_asprintf_append(
222 } else if (strcmp(type, "sl_time_t") == 0) {
223 memcpy(&t, p, sizeof(sl_time_t));
224 tm = localtime(&t.tv_sec);
228 result = strftime(datestring,
230 "%Y-%m-%d %H:%M:%S", tm);
234 logstring = talloc_asprintf_append(
236 "%ssl_time_t: %s.%06lu\n",
239 (unsigned long)t.tv_usec);
240 } else if (strcmp(type, "sl_cnids_t") == 0) {
241 memcpy(&cnids, p, sizeof(sl_cnids_t));
242 logstring = talloc_asprintf_append(
244 "%sCNIDs: unkn1: 0x%" PRIx16 ", unkn2: 0x%" PRIx32 "\n",
248 if (logstring == NULL) {
251 if (cnids.ca_cnids) {
252 nested_logstring = mds_dalloc_dump(
255 if (!nested_logstring) {
258 logstring = talloc_strdup_append(logstring,
262 logstring = talloc_asprintf_append(
268 if (logstring == NULL) {
272 logstring = talloc_asprintf_append(logstring,
275 if (logstring == NULL) {
281 static char *tracker_to_unix_path(TALLOC_CTX *mem_ctx, const char *uri)
287 f = g_file_new_for_uri(uri);
292 path = g_file_get_path(f);
299 talloc_path = talloc_strdup(mem_ctx, path);
301 if (talloc_path == NULL) {
309 * Add requested metadata for a query result element
311 * This could be rewritten to something more sophisticated like
312 * querying metadata from Tracker.
314 * If path or sp is NULL, simply add nil values for all attributes.
316 static bool add_filemeta(sl_array_t *reqinfo,
317 sl_array_t *fm_array,
319 const struct stat_ex *sp)
323 int i, metacount, result;
327 const char *attribute;
329 metacount = dalloc_size(reqinfo);
330 if (metacount == 0 || path == NULL || sp == NULL) {
331 result = dalloc_add_copy(fm_array, &nil, sl_nil_t);
338 meta = dalloc_zero(fm_array, sl_array_t);
343 for (i = 0; i < metacount; i++) {
344 attribute = dalloc_get_object(reqinfo, i);
345 if (attribute == NULL) {
348 if (strcmp(attribute, "kMDItemDisplayName") == 0
349 || strcmp(attribute, "kMDItemFSName") == 0) {
350 p = strrchr(path, '/');
352 result = dalloc_stradd(meta, p + 1);
357 } else if (strcmp(attribute, "kMDItemPath") == 0) {
358 result = dalloc_stradd(meta, path);
362 } else if (strcmp(attribute, "kMDItemFSSize") == 0) {
363 uint64var = sp->st_ex_size;
364 result = dalloc_add_copy(meta, &uint64var, uint64_t);
368 } else if (strcmp(attribute, "kMDItemFSOwnerUserID") == 0) {
369 uint64var = sp->st_ex_uid;
370 result = dalloc_add_copy(meta, &uint64var, uint64_t);
374 } else if (strcmp(attribute, "kMDItemFSOwnerGroupID") == 0) {
375 uint64var = sp->st_ex_gid;
376 result = dalloc_add_copy(meta, &uint64var, uint64_t);
380 } else if (strcmp(attribute, "kMDItemFSContentChangeDate") == 0) {
381 sl_time.tv_sec = sp->st_ex_mtime.tv_sec;
382 result = dalloc_add_copy(meta, &sl_time, sl_time_t);
387 result = dalloc_add_copy(meta, &nil, sl_nil_t);
394 result = dalloc_add(fm_array, meta, sl_array_t);
401 static int cnid_comp_fn(const void *p1, const void *p2)
403 const uint64_t *cnid1 = p1, *cnid2 = p2;
404 if (*cnid1 == *cnid2) {
407 if (*cnid1 < *cnid2) {
414 * Create a sorted copy of a CNID array
416 static bool sort_cnids(struct sl_query *slq, const DALLOC_CTX *d)
418 uint64_t *cnids = NULL;
422 cnids = talloc_array(slq, uint64_t, dalloc_size(d));
427 for (i = 0; i < dalloc_size(d); i++) {
428 p = dalloc_get_object(d, i);
432 memcpy(&cnids[i], p, sizeof(uint64_t));
434 qsort(cnids, dalloc_size(d), sizeof(uint64_t), cnid_comp_fn);
437 slq->cnids_num = dalloc_size(d);
443 * Allocate result handle used in the async Tracker cursor result
444 * handler for storing results
446 static bool create_result_handle(struct sl_query *slq)
449 struct sl_rslts *query_results;
452 if (slq->query_results) {
453 DEBUG(1, ("unexpected existing result handle\n"));
457 query_results = talloc_zero(slq, struct sl_rslts);
458 if (query_results == NULL) {
463 query_results->cnids = talloc_zero(query_results, sl_cnids_t);
464 if (query_results->cnids == NULL) {
467 query_results->cnids->ca_cnids = dalloc_new(query_results->cnids);
468 if (query_results->cnids->ca_cnids == NULL) {
472 query_results->cnids->ca_unkn1 = 0xadd;
473 if (slq->ctx2 > UINT32_MAX) {
474 DEBUG(1,("64bit ctx2 id too large: 0x%jx", (uintmax_t)slq->ctx2));
477 query_results->cnids->ca_context = (uint32_t)slq->ctx2;
480 query_results->fm_array = dalloc_zero(query_results, sl_array_t);
481 if (query_results->fm_array == NULL) {
485 /* For some reason the list of results always starts with a nil entry */
486 result = dalloc_add_copy(query_results->fm_array, &nil, sl_nil_t);
491 slq->query_results = query_results;
495 static bool add_results(sl_array_t *array, struct sl_query *slq)
503 fm = dalloc_zero(array, sl_filemeta_t);
508 result = dalloc_add_copy(array, &status, uint64_t);
512 result = dalloc_add(array, slq->query_results->cnids, sl_cnids_t);
516 if (slq->query_results->num_results > 0) {
517 result = dalloc_add(fm, slq->query_results->fm_array, sl_array_t);
522 result = dalloc_add(array, fm, sl_filemeta_t);
527 /* This ensure the results get clean up after been sent to the client */
528 talloc_move(array, &slq->query_results);
530 ok = create_result_handle(slq);
532 DEBUG(1, ("couldn't add result handle\n"));
533 slq->state = SLQ_STATE_ERROR;
540 static const struct slrpc_cmd *slrpc_cmd_by_name(const char *rpccmd)
543 static const struct slrpc_cmd cmds[] = {
544 { "fetchPropertiesForContext:", slrpc_fetch_properties},
545 { "openQueryWithParams:forContext:", slrpc_open_query},
546 { "fetchQueryResultsForContext:", slrpc_fetch_query_results},
547 { "storeAttributes:forOIDArray:context:", slrpc_store_attributes},
548 { "fetchAttributeNamesForOIDArray:context:", slrpc_fetch_attributenames},
549 { "fetchAttributes:forOIDArray:context:", slrpc_fetch_attributes},
550 { "fetchAllAttributes:forOIDArray:context:", slrpc_fetch_attributes},
551 { "closeQueryForContext:", slrpc_close_query},
554 for (i = 0; i < ARRAY_SIZE(cmds); i++) {
557 cmp = strcmp(cmds[i].name, rpccmd);
567 * Search the list of active queries given their context ids
569 static struct sl_query *slq_for_ctx(struct mds_ctx *mds_ctx,
570 uint64_t ctx1, uint64_t ctx2)
574 for (q = mds_ctx->query_list; q; q = q->next) {
575 if ((q->ctx1 == ctx1) && (q->ctx2 == ctx2)) {
583 static int slq_destructor_cb(struct sl_query *slq)
585 SLQ_DEBUG(10, slq, "destroying");
587 /* Free all entries before freeing the slq handle! */
588 TALLOC_FREE(slq->entries_ctx);
589 TALLOC_FREE(slq->te);
591 if (slq->mds_ctx != NULL) {
592 DLIST_REMOVE(slq->mds_ctx->query_list, slq);
596 if (slq->tracker_cursor != NULL) {
597 g_object_unref(slq->tracker_cursor);
598 slq->tracker_cursor = NULL;
601 if (slq->gcancellable != NULL) {
602 g_cancellable_cancel(slq->gcancellable);
603 g_object_unref(slq->gcancellable);
604 slq->gcancellable = NULL;
611 * Remove talloc_refcounted entry from mapping db
613 * Multiple queries (via the slq handle) may reference a
614 * sl_inode_path_map entry, when the last reference goes away as the
615 * queries are closed and this gets called to remove the entry from
618 static int ino_path_map_destr_cb(struct sl_inode_path_map *entry)
623 key = make_tdb_data((uint8_t *)&entry->ino, sizeof(entry->ino));
625 status = dbwrap_delete(entry->mds_ctx->ino_path_map, key);
626 if (!NT_STATUS_IS_OK(status)) {
627 DEBUG(1, ("Failed to delete record: %s\n", nt_errstr(status)));
631 DEBUG(10,("deleted: %s\n", entry->path));
636 * Add result to inode->path mapping dbwrap rbt db
638 * This is necessary as a CNID db substitute, ie we need a way to
639 * simulate unique, constant numerical identifiers for paths with an
640 * API that supports mapping from id to path.
642 * Entries are talloc'ed of the query, using talloc_reference() if
643 * multiple queries returned the same result. That way we can cleanup
644 * entries by calling talloc_free() on the query slq handles.
647 static bool inode_map_add(struct sl_query *slq, uint64_t ino, const char *path)
650 struct sl_inode_path_map *entry;
654 key = make_tdb_data((uint8_t *)&ino, sizeof(ino));
655 status = dbwrap_fetch(slq->mds_ctx->ino_path_map, slq, key, &value);
657 if (NT_STATUS_IS_OK(status)) {
659 * We have one db, so when different parallel queries
660 * return the same file, we have to refcount entries
664 if (value.dsize != sizeof(void *)) {
665 DEBUG(1, ("invalide dsize\n"));
668 memcpy(&p, value.dptr, sizeof(p));
669 entry = talloc_get_type_abort(p, struct sl_inode_path_map);
671 DEBUG(10, ("map: %s\n", entry->path));
673 entry = talloc_reference(slq->entries_ctx, entry);
675 DEBUG(1, ("talloc_reference failed\n"));
681 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
682 DEBUG(1, ("dbwrap_fetch failed %s\n", nt_errstr(status)));
686 entry = talloc_zero(slq->entries_ctx, struct sl_inode_path_map);
688 DEBUG(1, ("talloc failed\n"));
693 entry->mds_ctx = slq->mds_ctx;
694 entry->path = talloc_strdup(entry, path);
695 if (entry->path == NULL) {
696 DEBUG(1, ("talloc failed\n"));
701 status = dbwrap_store(slq->mds_ctx->ino_path_map, key,
702 make_tdb_data((void *)&entry, sizeof(void *)), 0);
703 if (!NT_STATUS_IS_OK(status)) {
704 DEBUG(1, ("Failed to store record: %s\n", nt_errstr(status)));
709 talloc_set_destructor(entry, ino_path_map_destr_cb);
714 /************************************************
715 * Tracker async callbacks
716 ************************************************/
718 static void tracker_con_cb(GObject *object,
722 struct mds_ctx *mds_ctx = talloc_get_type_abort(user_data, struct mds_ctx);
723 GError *error = NULL;
725 mds_ctx->tracker_con = tracker_sparql_connection_get_finish(res,
728 DEBUG(1, ("Could not connect to Tracker: %s\n",
733 DEBUG(10, ("connected to Tracker\n"));
734 g_main_loop_quit(mds_ctx->gmainloop);
737 static void tracker_cursor_cb_destroy_done(struct tevent_req *subreq);
739 static void tracker_cursor_cb(GObject *object,
743 GError *error = NULL;
744 struct sl_query *slq = talloc_get_type_abort(user_data, struct sl_query);
745 gboolean more_results;
752 struct tevent_req *req;
754 SLQ_DEBUG(10, slq, "tracker_cursor_cb");
756 more_results = tracker_sparql_cursor_next_finish(slq->tracker_cursor,
760 if (slq->state == SLQ_STATE_DONE) {
762 * The query was closed in slrpc_close_query(), so we
763 * don't care for results or errors from
764 * tracker_sparql_cursor_next_finish(), we just go
765 * ahead and schedule deallocation of the slq handle.
767 * We have to shedule the deallocation via tevent,
768 * because we have to unref the cursor glib object and
769 * we can't do it here, because it's still used after
772 SLQ_DEBUG(10, slq, "closed");
773 g_main_loop_quit(slq->mds_ctx->gmainloop);
775 req = slq_destroy_send(slq, server_event_context(), &slq);
777 slq->state = SLQ_STATE_ERROR;
780 tevent_req_set_callback(req, tracker_cursor_cb_destroy_done, NULL);
785 DEBUG(1, ("Tracker cursor: %s\n", error->message));
787 slq->state = SLQ_STATE_ERROR;
788 g_main_loop_quit(slq->mds_ctx->gmainloop);
793 slq->state = SLQ_STATE_DONE;
794 g_main_loop_quit(slq->mds_ctx->gmainloop);
798 uri = tracker_sparql_cursor_get_string(slq->tracker_cursor, 0, NULL);
800 DEBUG(1, ("error fetching Tracker URI\n"));
801 slq->state = SLQ_STATE_ERROR;
802 g_main_loop_quit(slq->mds_ctx->gmainloop);
805 path = tracker_to_unix_path(slq->query_results, uri);
807 DEBUG(1, ("error converting Tracker URI to path: %s\n", uri));
808 slq->state = SLQ_STATE_ERROR;
809 g_main_loop_quit(slq->mds_ctx->gmainloop);
813 if (geteuid() != slq->mds_ctx->uid) {
814 DEBUG(0, ("uid mismatch: %d/%d\n", geteuid(), slq->mds_ctx->uid));
815 smb_panic("uid mismatch");
818 result = sys_stat(path, &sb, false);
822 result = access(path, R_OK);
827 ino64 = sb.st_ex_ino;
830 * Check whether the found element is in the requested
831 * set of IDs. Note that we're faking CNIDs by using
832 * filesystem inode numbers here
834 ok = bsearch(&ino64, slq->cnids, slq->cnids_num,
835 sizeof(uint64_t), cnid_comp_fn);
842 * Add inode number and filemeta to result set, this is what
843 * we return as part of the result set of a query
845 result = dalloc_add_copy(slq->query_results->cnids->ca_cnids,
848 DEBUG(1, ("dalloc error\n"));
849 slq->state = SLQ_STATE_ERROR;
850 g_main_loop_quit(slq->mds_ctx->gmainloop);
853 ok = add_filemeta(slq->reqinfo, slq->query_results->fm_array,
856 DEBUG(1, ("add_filemeta error\n"));
857 slq->state = SLQ_STATE_ERROR;
858 g_main_loop_quit(slq->mds_ctx->gmainloop);
862 ok = inode_map_add(slq, ino64, path);
864 DEBUG(1, ("inode_map_add error\n"));
865 slq->state = SLQ_STATE_ERROR;
866 g_main_loop_quit(slq->mds_ctx->gmainloop);
870 slq->query_results->num_results++;
873 if (slq->query_results->num_results >= MAX_SL_RESULTS) {
874 slq->state = SLQ_STATE_FULL;
875 SLQ_DEBUG(10, slq, "full");
876 g_main_loop_quit(slq->mds_ctx->gmainloop);
880 slq->state = SLQ_STATE_RESULTS;
881 SLQ_DEBUG(10, slq, "cursor next");
882 tracker_sparql_cursor_next_async(slq->tracker_cursor,
888 static void tracker_cursor_cb_destroy_done(struct tevent_req *req)
890 slq_destroy_recv(req);
893 DEBUG(10, ("%s\n", __func__));
896 static void tracker_query_cb(GObject *object,
900 GError *error = NULL;
901 struct sl_query *slq = talloc_get_type_abort(user_data, struct sl_query);
903 SLQ_DEBUG(10, slq, "tracker_query_cb");
905 slq->tracker_cursor = tracker_sparql_connection_query_finish(
906 TRACKER_SPARQL_CONNECTION(object),
910 slq->state = SLQ_STATE_ERROR;
911 DEBUG(1, ("Tracker query error: %s\n", error->message));
913 g_main_loop_quit(slq->mds_ctx->gmainloop);
917 if (slq->state == SLQ_STATE_DONE) {
918 SLQ_DEBUG(10, slq, "done");
919 g_main_loop_quit(slq->mds_ctx->gmainloop);
924 slq->state = SLQ_STATE_RESULTS;
926 tracker_sparql_cursor_next_async(slq->tracker_cursor,
932 /***********************************************************
933 * Spotlight RPC functions
934 ***********************************************************/
936 static bool slrpc_fetch_properties(struct mds_ctx *mds_ctx,
937 const DALLOC_CTX *query, DALLOC_CTX *reply)
947 dict = dalloc_zero(reply, sl_dict_t);
952 /* kMDSStoreHasPersistentUUID = false */
953 result = dalloc_stradd(dict, "kMDSStoreHasPersistentUUID");
958 result = dalloc_add_copy(dict, &b, sl_bool_t);
963 /* kMDSStoreIsBackup = false */
964 result = dalloc_stradd(dict, "kMDSStoreIsBackup");
969 result = dalloc_add_copy(dict, &b, sl_bool_t);
974 /* kMDSStoreUUID = uuid */
975 result = dalloc_stradd(dict, "kMDSStoreUUID");
979 memcpy(uuid.sl_uuid, "fakeuuidfakeuuid", sizeof(uuid.sl_uuid));
980 result = dalloc_add_copy(dict, &uuid, sl_uuid_t);
985 /* kMDSStoreSupportsVolFS = true */
986 result = dalloc_stradd(dict, "kMDSStoreSupportsVolFS");
991 result = dalloc_add_copy(dict, &b, sl_bool_t);
996 /* kMDSVolumeUUID = uuid */
997 result = dalloc_stradd(dict, "kMDSVolumeUUID");
1001 memcpy(uuid.sl_uuid, "fakeuuidfakeuuid", sizeof(uuid.sl_uuid));
1002 result = dalloc_add_copy(dict, &uuid, sl_uuid_t);
1007 /* kMDSDiskStoreSpindleNumber = 1 (fake) */
1008 result = dalloc_stradd(dict, "kMDSDiskStoreSpindleNumber");
1013 result = dalloc_add_copy(dict, &u, uint64_t);
1018 /* kMDSDiskStorePolicy = 3 (whatever that means, taken from OS X) */
1019 result = dalloc_stradd(dict, "kMDSDiskStorePolicy");
1024 result = dalloc_add_copy(dict, &u, uint64_t);
1029 /* kMDSStoreMetaScopes array */
1030 array = dalloc_zero(dict, sl_array_t);
1031 if (array == NULL) {
1034 result = dalloc_stradd(array, "kMDQueryScopeComputer");
1038 result = dalloc_stradd(array, "kMDQueryScopeAllIndexed");
1042 result = dalloc_stradd(array, "kMDQueryScopeComputerIndexed");
1046 result = dalloc_add(dict, array, sl_array_t);
1051 /* kMDSStoreDevice = 0x1000003 (whatever that means, taken from OS X) */
1052 result = dalloc_stradd(dict, "kMDSStoreDevice");
1057 result = dalloc_add_copy(dict, &u, uint64_t);
1062 /* kMDSStoreSupportsTCC = true (whatever that means, taken from OS X) */
1063 result = dalloc_stradd(dict, "kMDSStoreSupportsTCC");
1068 result = dalloc_add_copy(dict, &b, sl_bool_t);
1073 /* kMDSStorePathScopes = ["/"] (whatever that means, taken from OS X) */
1074 result = dalloc_stradd(dict, "kMDSStorePathScopes");
1078 array = dalloc_zero(dict, sl_array_t);
1079 if (array == NULL) {
1082 s = talloc_strdup(dict, "/");
1086 talloc_set_name(s, "smb_ucs2_t *");
1087 result = dalloc_add(array, s, smb_ucs2_t *);
1091 result = dalloc_add(dict, array, sl_array_t);
1096 result = dalloc_add(reply, dict, sl_dict_t);
1104 static void slq_close_timer(struct tevent_context *ev,
1105 struct tevent_timer *te,
1106 struct timeval current_time,
1109 struct sl_query *slq = talloc_get_type_abort(
1110 private_data, struct sl_query);
1111 struct mds_ctx *mds_ctx = slq->mds_ctx;
1113 SLQ_DEBUG(10, slq, "expired");
1117 if (CHECK_DEBUGLVL(10)) {
1118 for (slq = mds_ctx->query_list; slq != NULL; slq = slq->next) {
1119 SLQ_DEBUG(10, slq, "pending");
1125 * Begin a search query
1127 static bool slrpc_open_query(struct mds_ctx *mds_ctx,
1128 const DALLOC_CTX *query, DALLOC_CTX *reply)
1133 DALLOC_CTX *reqinfo;
1134 sl_array_t *array, *path_scope;
1136 struct sl_query *slq = NULL;
1140 array = dalloc_zero(reply, sl_array_t);
1141 if (array == NULL) {
1145 if (mds_ctx->tracker_con == NULL) {
1146 DEBUG(1, ("no connection to Tracker\n"));
1150 /* Allocate and initialize query object */
1151 slq = talloc_zero(mds_ctx, struct sl_query);
1155 slq->entries_ctx = talloc_named_const(slq, 0, "struct sl_query.entries_ctx");
1156 if (slq->entries_ctx == NULL) {
1160 talloc_set_destructor(slq, slq_destructor_cb);
1161 slq->state = SLQ_STATE_NEW;
1162 slq->mds_ctx = mds_ctx;
1164 slq->last_used = timeval_current();
1165 slq->start_time = slq->last_used;
1166 slq->expire_time = timeval_add(&slq->last_used, MAX_SL_RUNTIME, 0);
1167 slq->te = tevent_add_timer(server_event_context(), slq,
1168 slq->expire_time, slq_close_timer, slq);
1169 if (slq->te == NULL) {
1170 DEBUG(1, ("tevent_add_timer failed\n"));
1174 slq->gcancellable = g_cancellable_new();
1175 if (slq->gcancellable == NULL) {
1176 DEBUG(1,("error from g_cancellable_new\n"));
1180 querystring = dalloc_value_for_key(query, "DALLOC_CTX", 0,
1183 if (querystring == NULL) {
1184 DEBUG(1, ("missing kMDQueryString\n"));
1187 slq->query_string = talloc_strdup(slq, querystring);
1188 if (slq->query_string == NULL) {
1189 DEBUG(1, ("out of memory\n"));
1194 * FIXME: convert spotlight query charset from decomposed UTF8
1195 * to host charset precomposed UTF8.
1198 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1200 if (uint64p == NULL) {
1203 slq->ctx1 = *uint64p;
1204 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1206 if (uint64p == NULL) {
1209 slq->ctx2 = *uint64p;
1211 path_scope = dalloc_value_for_key(query, "DALLOC_CTX", 0,
1212 "DALLOC_CTX", 1, "kMDScopeArray");
1213 if (path_scope == NULL) {
1217 slq->path_scope = dalloc_get(path_scope, "char *", 0);
1218 if (slq->path_scope == NULL) {
1222 slq->path_scope = talloc_strdup(slq, slq->path_scope);
1223 if (slq->path_scope == NULL) {
1228 reqinfo = dalloc_value_for_key(query, "DALLOC_CTX", 0,
1229 "DALLOC_CTX", 1, "kMDAttributeArray");
1230 if (reqinfo == NULL) {
1234 slq->reqinfo = talloc_steal(slq, reqinfo);
1235 DEBUG(10, ("requested attributes: %s", mds_dalloc_dump(reqinfo, 0)));
1237 cnids = dalloc_value_for_key(query, "DALLOC_CTX", 0,
1238 "DALLOC_CTX", 1, "kMDQueryItemArray");
1240 ok = sort_cnids(slq, cnids->ca_cnids);
1246 ok = create_result_handle(slq);
1248 DEBUG(1, ("create_result_handle error\n"));
1249 slq->state = SLQ_STATE_ERROR;
1253 SLQ_DEBUG(10, slq, "new");
1255 DLIST_ADD(mds_ctx->query_list, slq);
1257 ok = map_spotlight_to_sparql_query(slq);
1262 * 1) the query string is "false", the parser returns
1263 * an error for that. We're supposed to return -1
1266 * 2) the parsing really failed, in that case we're
1267 * probably supposed to return -1 too, this needs
1268 * verification though
1270 SLQ_DEBUG(10, slq, "map failed");
1274 DEBUG(10, ("SPARQL query: \"%s\"\n", slq->sparql_query));
1276 g_main_context_push_thread_default(mds_ctx->gcontext);
1277 tracker_sparql_connection_query_async(mds_ctx->tracker_con,
1282 g_main_context_pop_thread_default(mds_ctx->gcontext);
1283 slq->state = SLQ_STATE_RUNNING;
1286 result = dalloc_add_copy(array, &sl_result, uint64_t);
1290 result = dalloc_add(reply, array, sl_array_t);
1297 sl_result = UINT64_MAX;
1299 result = dalloc_add_copy(array, &sl_result, uint64_t);
1303 result = dalloc_add(reply, array, sl_array_t);
1311 * Fetch results of a query
1313 static bool slrpc_fetch_query_results(struct mds_ctx *mds_ctx,
1314 const DALLOC_CTX *query,
1318 struct sl_query *slq = NULL;
1319 uint64_t *uint64p, ctx1, ctx2;
1324 array = dalloc_zero(reply, sl_array_t);
1325 if (array == NULL) {
1329 /* Get query for context */
1330 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1332 if (uint64p == NULL) {
1337 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1339 if (uint64p == NULL) {
1344 slq = slq_for_ctx(mds_ctx, ctx1, ctx2);
1346 DEBUG(1, ("bad context: [0x%jx,0x%jx]\n",
1347 (uintmax_t)ctx1, (uintmax_t)ctx2));
1351 TALLOC_FREE(slq->te);
1352 slq->last_used = timeval_current();
1353 slq->expire_time = timeval_add(&slq->last_used, MAX_SL_RUNTIME, 0);
1354 slq->te = tevent_add_timer(server_event_context(), slq,
1355 slq->expire_time, slq_close_timer, slq);
1356 if (slq->te == NULL) {
1357 DEBUG(1, ("tevent_add_timer failed\n"));
1361 SLQ_DEBUG(10, slq, "fetch");
1363 switch (slq->state) {
1364 case SLQ_STATE_RUNNING:
1365 case SLQ_STATE_RESULTS:
1366 case SLQ_STATE_FULL:
1367 case SLQ_STATE_DONE:
1368 ok = add_results(array, slq);
1370 DEBUG(1, ("error adding results\n"));
1373 if (slq->state == SLQ_STATE_FULL) {
1374 slq->state = SLQ_STATE_RESULTS;
1375 g_main_context_push_thread_default(mds_ctx->gcontext);
1376 tracker_sparql_cursor_next_async(
1377 slq->tracker_cursor,
1381 g_main_context_pop_thread_default(mds_ctx->gcontext);
1385 case SLQ_STATE_ERROR:
1386 DEBUG(1, ("query in error state\n"));
1390 DEBUG(1, ("unexpected query state %d\n", slq->state));
1394 result = dalloc_add(reply, array, sl_array_t);
1401 status = UINT64_MAX;
1403 result = dalloc_add_copy(array, &status, uint64_t);
1407 result = dalloc_add(reply, array, sl_array_t);
1415 * Store metadata attributes for a CNID
1417 static bool slrpc_store_attributes(struct mds_ctx *mds_ctx,
1418 const DALLOC_CTX *query, DALLOC_CTX *reply)
1424 array = dalloc_zero(reply, sl_array_t);
1425 if (array == NULL) {
1430 * FIXME: not implemented. Used by the client for eg setting
1431 * the modification date of the shared directory which clients
1432 * poll indicating changes on the share and cause the client
1437 result = dalloc_add_copy(array, &sl_result, uint64_t);
1441 result = dalloc_add(reply, array, sl_array_t);
1450 * Fetch supported metadata attributes for a CNID
1452 static bool slrpc_fetch_attributenames(struct mds_ctx *mds_ctx,
1453 const DALLOC_CTX *query,
1460 sl_cnids_t *replycnids;
1461 sl_array_t *mdattrs;
1462 sl_filemeta_t *fmeta;
1466 cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 1);
1467 if (cnids == NULL) {
1471 p = dalloc_get_object(cnids->ca_cnids, 0);
1475 memcpy(&id, p, sizeof(uint64_t));
1478 array = dalloc_zero(reply, sl_array_t);
1479 if (array == NULL) {
1483 result = dalloc_add(reply, array, sl_array_t);
1488 /* Return result value 0 */
1490 result = dalloc_add_copy(array, &sl_result, uint64_t);
1495 /* Return CNID array */
1496 replycnids = talloc_zero(reply, sl_cnids_t);
1497 if (replycnids == NULL) {
1501 replycnids->ca_cnids = dalloc_new(cnids);
1502 if (replycnids->ca_cnids == NULL) {
1506 replycnids->ca_unkn1 = 0xfec;
1507 replycnids->ca_context = cnids->ca_context;
1508 result = dalloc_add_copy(replycnids->ca_cnids, &id, uint64_t);
1512 result = dalloc_add(array, replycnids, sl_cnids_t);
1518 * FIXME: this should return the real attributes from all
1519 * known metadata sources (Tracker and filesystem)
1521 mdattrs = dalloc_zero(reply, sl_array_t);
1522 if (mdattrs == NULL) {
1526 result = dalloc_stradd(mdattrs, "kMDItemFSName");
1530 result = dalloc_stradd(mdattrs, "kMDItemDisplayName");
1534 result = dalloc_stradd(mdattrs, "kMDItemFSSize");
1538 result = dalloc_stradd(mdattrs, "kMDItemFSOwnerUserID");
1542 result = dalloc_stradd(mdattrs, "kMDItemFSOwnerGroupID");
1546 result = dalloc_stradd(mdattrs, "kMDItemFSContentChangeDate");
1551 fmeta = dalloc_zero(reply, sl_filemeta_t);
1552 if (fmeta == NULL) {
1555 result = dalloc_add(fmeta, mdattrs, sl_array_t);
1559 result = dalloc_add(array, fmeta, sl_filemeta_t);
1568 * Fetch metadata attribute values for a CNID
1570 static bool slrpc_fetch_attributes(struct mds_ctx *mds_ctx,
1571 const DALLOC_CTX *query, DALLOC_CTX *reply)
1577 sl_cnids_t *replycnids;
1578 sl_array_t *reqinfo;
1582 sl_array_t *fm_array;
1585 struct sl_inode_path_map *elem = NULL;
1587 TDB_DATA val = tdb_null;
1590 array = dalloc_zero(reply, sl_array_t);
1591 if (array == NULL) {
1594 replycnids = talloc_zero(reply, sl_cnids_t);
1595 if (replycnids == NULL) {
1598 replycnids->ca_cnids = dalloc_new(replycnids);
1599 if (replycnids->ca_cnids == NULL) {
1602 fm = dalloc_zero(array, sl_filemeta_t);
1606 fm_array = dalloc_zero(fm, sl_array_t);
1607 if (fm_array == NULL) {
1610 /* For some reason the list of results always starts with a nil entry */
1611 result = dalloc_add_copy(fm_array, &nil, sl_nil_t);
1616 reqinfo = dalloc_get(query, "DALLOC_CTX", 0, "sl_array_t", 1);
1617 if (reqinfo == NULL) {
1621 cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 2);
1622 if (cnids == NULL) {
1625 p = dalloc_get_object(cnids->ca_cnids, 0);
1629 memcpy(&ino, p, sizeof(uint64_t));
1631 replycnids->ca_unkn1 = 0xfec;
1632 replycnids->ca_context = cnids->ca_context;
1633 result = dalloc_add_copy(replycnids->ca_cnids, &ino, uint64_t);
1638 status = dbwrap_fetch(mds_ctx->ino_path_map, reply,
1639 make_tdb_data((void*)&ino, sizeof(uint64_t)),
1641 if (!NT_STATUS_IS_OK(status)) {
1642 DEBUG(1, ("Failed to fetch inode: %s\n", nt_errstr(status)));
1645 if (val.dsize != sizeof(p)) {
1646 DEBUG(1, ("invalid record pointer size: %zd\n", val.dsize));
1647 TALLOC_FREE(val.dptr);
1651 memcpy(&p, val.dptr, sizeof(p));
1652 elem = talloc_get_type_abort(p, struct sl_inode_path_map);
1654 result = sys_stat(elem->path, &sb, false);
1659 ok = add_filemeta(reqinfo, fm_array, elem->path, &sb);
1665 result = dalloc_add_copy(array, &sl_result, uint64_t);
1669 result = dalloc_add(array, replycnids, sl_cnids_t);
1673 result = dalloc_add(fm, fm_array, sl_array_t);
1677 result = dalloc_add(array, fm, sl_filemeta_t);
1681 result = dalloc_add(reply, array, sl_array_t);
1689 sl_result = UINT64_MAX;
1690 result = dalloc_add_copy(array, &sl_result, uint64_t);
1694 result = dalloc_add(reply, array, sl_array_t);
1705 static bool slrpc_close_query(struct mds_ctx *mds_ctx,
1706 const DALLOC_CTX *query, DALLOC_CTX *reply)
1708 struct sl_query *slq = NULL;
1709 uint64_t *uint64p, ctx1, ctx2;
1714 array = dalloc_zero(reply, sl_array_t);
1715 if (array == NULL) {
1720 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1722 if (uint64p == NULL) {
1727 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1729 if (uint64p == NULL) {
1734 /* Get query for context and free it */
1735 slq = slq_for_ctx(mds_ctx, ctx1, ctx2);
1737 DEBUG(1, ("bad context: [0x%jx,0x%jx]\n",
1738 (uintmax_t)ctx1, (uintmax_t)ctx2));
1742 switch (slq->state) {
1743 case SLQ_STATE_RUNNING:
1744 case SLQ_STATE_RESULTS:
1745 DEBUG(10, ("close: requesting query close\n"));
1747 * Mark the query is done so the cursor callback can
1748 * act accordingly by stopping to request more results
1749 * and sheduling query resource deallocation via
1752 slq->state = SLQ_STATE_DONE;
1755 case SLQ_STATE_FULL:
1756 case SLQ_STATE_DONE:
1757 DEBUG(10, ("close: query was done or result queue was full\n"));
1759 * We can directly deallocate the query because there
1760 * are no pending Tracker async calls in flight in
1761 * these query states.
1767 DEBUG(1, ("close: unexpected state: %d\n", slq->state));
1774 result = dalloc_add_copy(array, &sl_res, uint64_t);
1778 result = dalloc_add(reply, array, sl_array_t);
1786 * Init callbacks at startup
1788 bool mds_init(struct messaging_context *msg_ctx)
1790 #if (GLIB_MAJOR_VERSION < 3) && (GLIB_MINOR_VERSION < 36)
1796 bool mds_shutdown(void)
1801 static gboolean gmainloop_timer(gpointer user_data)
1803 struct mds_ctx *ctx = talloc_get_type_abort(user_data, struct mds_ctx);
1805 DEBUG(10,("%s\n", __func__));
1806 g_main_loop_quit(ctx->gmainloop);
1808 return G_SOURCE_CONTINUE;
1812 * Initialise a context per share handle
1814 struct mds_ctx *mds_init_ctx(TALLOC_CTX *mem_ctx,
1815 const struct auth_session_info *session_info,
1818 struct mds_ctx *mds_ctx;
1820 mds_ctx = talloc_zero(mem_ctx, struct mds_ctx);
1821 if (mds_ctx == NULL) {
1824 talloc_set_destructor(mds_ctx, mds_ctx_destructor_cb);
1826 mds_ctx->spath = talloc_strdup(mds_ctx, path);
1827 if (mds_ctx->spath == NULL) {
1831 if (session_info->security_token->num_sids < 1) {
1834 sid_copy(&mds_ctx->sid, &session_info->security_token->sids[0]);
1835 mds_ctx->uid = session_info->unix_token->uid;
1837 mds_ctx->ino_path_map = db_open_rbt(mds_ctx);
1838 if (mds_ctx->ino_path_map == NULL) {
1839 DEBUG(1,("open inode map db failed\n"));
1843 mds_ctx->gcontext = g_main_context_new();
1844 if (mds_ctx->gcontext == NULL) {
1845 DEBUG(1,("error from g_main_context_new\n"));
1849 mds_ctx->gmainloop = g_main_loop_new(mds_ctx->gcontext, false);
1850 if (mds_ctx->gmainloop == NULL) {
1851 DEBUG(1,("error from g_main_loop_new\n"));
1855 g_main_context_push_thread_default(mds_ctx->gcontext);
1856 tracker_sparql_connection_get_async(mds_ctx->gcancellable,
1857 tracker_con_cb, mds_ctx);
1858 g_main_context_pop_thread_default(mds_ctx->gcontext);
1863 TALLOC_FREE(mds_ctx);
1868 * Tear down connections and free all resources
1870 int mds_ctx_destructor_cb(struct mds_ctx *mds_ctx)
1873 * We need to free query_list before ino_path_map
1875 while (mds_ctx->query_list != NULL) {
1877 * slq destructor removes element from list.
1878 * Don't use TALLOC_FREE()!
1880 talloc_free(mds_ctx->query_list);
1882 TALLOC_FREE(mds_ctx->ino_path_map);
1884 if (mds_ctx->tracker_con != NULL) {
1885 g_object_unref(mds_ctx->tracker_con);
1887 if (mds_ctx->gcancellable != NULL) {
1888 g_cancellable_cancel(mds_ctx->gcancellable);
1889 g_object_unref(mds_ctx->gcancellable);
1891 if (mds_ctx->gmainloop != NULL) {
1892 g_main_loop_unref(mds_ctx->gmainloop);
1894 if (mds_ctx->gcontext != NULL) {
1895 g_main_context_unref(mds_ctx->gcontext);
1898 ZERO_STRUCTP(mds_ctx);
1903 static bool mds_run_gmainloop(struct mds_ctx *mds_ctx, guint timeout)
1909 * It seems the event processing of the libtracker-sparql
1910 * async subsystem defers callbacks until *all* events are
1911 * processes by the async subsystem main processing loop.
1913 * g_main_context_iteration(may_block=FALSE) can't be used,
1914 * because a search that produces a few thousand matches
1915 * generates as many events that must be processed in either
1916 * g_main_context_iteration() or g_main_loop_run() before
1917 * callbacks are called.
1919 * Unfortunately g_main_context_iteration() only processes a
1920 * small subset of these event (1-30) at a time when run in
1921 * mds_dispatch(), which happens once a second while the
1922 * client polls for results.
1924 * Carefully using the blocking g_main_loop_run() fixes
1925 * this. It processes events until we exit from the loop at
1926 * defined exit points. By adding a 1 ms timeout we at least
1927 * try to get as close as possible to non-blocking behaviour.
1930 if (!g_main_context_pending(mds_ctx->gcontext)) {
1934 g_main_context_push_thread_default(mds_ctx->gcontext);
1936 timer = g_timeout_source_new(timeout);
1937 if (timer == NULL) {
1938 DEBUG(1,("g_timeout_source_new_seconds\n"));
1939 g_main_context_pop_thread_default(mds_ctx->gcontext);
1943 timer_id = g_source_attach(timer, mds_ctx->gcontext);
1944 if (timer_id == 0) {
1945 DEBUG(1,("g_timeout_add failed\n"));
1946 g_source_destroy(timer);
1947 g_main_context_pop_thread_default(mds_ctx->gcontext);
1951 g_source_set_callback(timer, gmainloop_timer, mds_ctx, NULL);
1953 g_main_loop_run(mds_ctx->gmainloop);
1955 g_source_destroy(timer);
1957 g_main_context_pop_thread_default(mds_ctx->gcontext);
1962 * Dispatch a Spotlight RPC command
1964 bool mds_dispatch(struct mds_ctx *mds_ctx,
1965 struct mdssvc_blob *request_blob,
1966 struct mdssvc_blob *response_blob)
1970 DALLOC_CTX *query = NULL;
1971 DALLOC_CTX *reply = NULL;
1973 const struct slrpc_cmd *slcmd;
1975 if (CHECK_DEBUGLVL(10)) {
1976 const struct sl_query *slq;
1978 for (slq = mds_ctx->query_list; slq != NULL; slq = slq->next) {
1979 SLQ_DEBUG(10, slq, "pending");
1983 response_blob->length = 0;
1986 * Process finished glib events.
1988 * FIXME: integrate with tevent instead of piggy packing it
1989 * onto the processing of new requests.
1991 * mds_dispatch() is called by the client a few times in a row:
1993 * - first in order to open/start a search query
1995 * - later in order to fetch results asynchronously, typically
1996 * once a second. If no results have been retrieved from the
1997 * search store (Tracker) yet, we return no results.
1998 * The client asks for more results every second as long
1999 * as the "Search Window" in the client gui is open.
2001 * - at some point the query is closed
2003 * This means we try to iterate through the glib event loop
2004 * before processing the request in order to get result
2005 * from tracker which can be returned to the client.
2008 ok = mds_run_gmainloop(mds_ctx, MDS_TRACKER_ASYNC_TIMEOUT_MS);
2013 DEBUG(10, ("share path: %s\n", mds_ctx->spath));
2015 query = dalloc_new(mds_ctx);
2016 if (query == NULL) {
2020 reply = dalloc_new(mds_ctx);
2021 if (reply == NULL) {
2026 ok = sl_unpack(query, (char *)request_blob->spotlight_blob,
2027 request_blob->length);
2029 DEBUG(1, ("error unpacking Spotlight RPC blob\n"));
2033 DEBUG(5, ("%s", mds_dalloc_dump(query, 0)));
2035 rpccmd = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
2037 if (rpccmd == NULL) {
2038 DEBUG(1, ("missing primary Spotlight RPC command\n"));
2043 DEBUG(10, ("Spotlight RPC cmd: %s\n", rpccmd));
2045 slcmd = slrpc_cmd_by_name(rpccmd);
2046 if (slcmd == NULL) {
2047 DEBUG(1, ("unsupported primary Spotlight RPC command %s\n",
2054 * If these functions return an error, they hit something like
2055 * a non recoverable talloc error
2057 ok = slcmd->function(mds_ctx, query, reply);
2059 DEBUG(1, ("error in Spotlight RPC handler\n"));
2063 DEBUG(5, ("%s", mds_dalloc_dump(reply, 0)));
2065 len = sl_pack(reply, (char *)response_blob->spotlight_blob,
2066 response_blob->size);
2068 DEBUG(1, ("error packing Spotlight RPC reply\n"));
2074 * Run g_main_loop a second time in order to dispatch events
2075 * that may have been queued at the libtracker-sparql level.
2076 * As we only want to dispatch (write out requests) but not
2077 * wait for anything, we use a much shorter timeout here.
2079 ok = mds_run_gmainloop(mds_ctx, MDS_TRACKER_ASYNC_TIMEOUT_MS / 10);
2084 response_blob->length = len;