rpc_server: Fix CID 1311342 Null pointer dereferences (REVERSE_INULL)
[Samba.git] / source3 / rpc_server / mdssvc / mdssvc.c
blobabfea433e05c722c7c4e15afb9c8f292f1699d09
1 /*
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/>.
21 #include "includes.h"
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"
29 #include "mdssvc.h"
30 #include "sparql_parser.h"
32 #undef DBGC_CLASS
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; \
38 const char *start; \
39 struct timeval_buf last_used_buf; \
40 const char *last_used; \
41 struct timeval_buf expire_buf; \
42 const char *expire; \
43 start = timeval_str_buf(&__slq->start_time, false, \
44 true, &start_buf); \
45 last_used = timeval_str_buf(&__slq->last_used, false, \
46 true, &last_used_buf); \
47 expire = timeval_str_buf(&__slq->expire_time, false, \
48 true, &expire_buf); \
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)); \
53 }} while(0)
55 struct slrpc_cmd {
56 const char *name;
57 bool (*function)(struct mds_ctx *mds_ctx,
58 const DALLOC_CTX *query,
59 DALLOC_CTX *reply);
62 struct slq_destroy_state {
63 struct tevent_context *ev;
64 struct sl_query *slq;
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);
96 if (req == NULL) {
97 return NULL;
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)
116 int i;
117 char *string = talloc_array(mem_ctx, char, level + 1);
119 for (i = 0; i < level; i++) {
120 string[i] = '\t';
123 string[i] = '\0';
124 return string;
127 char *mds_dalloc_dump(DALLOC_CTX *dd, int nestinglevel)
129 const char *type;
130 int n, result;
131 uint64_t i;
132 sl_bool_t bl;
133 sl_time_t t;
134 struct tm *tm;
135 char datestring[256];
136 sl_cnids_t cnids;
137 char *logstring, *nested_logstring;
138 char *tab_string1, *tab_string2;
139 void *p;
140 bool ok;
141 char *utf8string;
142 size_t utf8len;
144 tab_string1 = tab_level(dd, nestinglevel);
145 if (tab_string1 == NULL) {
146 return NULL;
148 tab_string2 = tab_level(dd, nestinglevel + 1);
149 if (tab_string2 == NULL) {
150 return NULL;
153 logstring = talloc_asprintf(dd,
154 "%s%s(#%lu): {\n",
155 tab_string1,
156 talloc_get_name(dd),
157 dalloc_size(dd));
158 if (logstring == NULL) {
159 return NULL;
162 for (n = 0; n < dalloc_size(dd); n++) {
163 type = dalloc_get_name(dd, n);
164 if (type == NULL) {
165 return NULL;
167 p = dalloc_get_object(dd, n);
168 if (p == NULL) {
169 return NULL;
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) {
177 return NULL;
179 logstring = talloc_strdup_append(logstring,
180 nested_logstring);
181 } else if (strcmp(type, "uint64_t") == 0) {
182 memcpy(&i, p, sizeof(uint64_t));
183 logstring = talloc_asprintf_append(
184 logstring,
185 "%suint64_t: 0x%04jx\n",
186 tab_string2, (uintmax_t)i);
187 } else if (strcmp(type, "char *") == 0) {
188 logstring = talloc_asprintf_append(
189 logstring,
190 "%sstring: %s\n",
191 tab_string2,
192 (char *)p);
193 } else if (strcmp(type, "smb_ucs2_t *") == 0) {
194 ok = convert_string_talloc(talloc_tos(),
195 CH_UTF16LE,
196 CH_UTF8,
198 talloc_get_size(p),
199 &utf8string,
200 &utf8len);
201 if (!ok) {
202 return NULL;
204 logstring = talloc_asprintf_append(
205 logstring,
206 "%sUTF16-string: %s\n",
207 tab_string2,
208 utf8string);
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(
213 logstring,
214 "%sbool: %s\n",
215 tab_string2,
216 bl ? "true" : "false");
217 } else if (strcmp(type, "sl_nil_t") == 0) {
218 logstring = talloc_asprintf_append(
219 logstring,
220 "%snil\n",
221 tab_string2);
222 } else if (strcmp(type, "sl_time_t") == 0) {
223 memcpy(&t, p, sizeof(sl_time_t));
224 tm = localtime(&t.tv_sec);
225 if (tm == NULL) {
226 return NULL;
228 result = strftime(datestring,
229 sizeof(datestring),
230 "%Y-%m-%d %H:%M:%S", tm);
231 if (result == 0) {
232 return NULL;
234 logstring = talloc_asprintf_append(
235 logstring,
236 "%ssl_time_t: %s.%06lu\n",
237 tab_string2,
238 datestring,
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(
243 logstring,
244 "%sCNIDs: unkn1: 0x%" PRIx16 ", unkn2: 0x%" PRIx32 "\n",
245 tab_string2,
246 cnids.ca_unkn1,
247 cnids.ca_context);
248 if (logstring == NULL) {
249 return NULL;
251 if (cnids.ca_cnids) {
252 nested_logstring = mds_dalloc_dump(
253 cnids.ca_cnids,
254 nestinglevel + 2);
255 if (!nested_logstring) {
256 return NULL;
258 logstring = talloc_strdup_append(logstring,
259 nested_logstring);
261 } else {
262 logstring = talloc_asprintf_append(
263 logstring,
264 "%stype: %s\n",
265 tab_string2,
266 type);
268 if (logstring == NULL) {
269 return NULL;
272 logstring = talloc_asprintf_append(logstring,
273 "%s}\n",
274 tab_string1);
275 if (logstring == NULL) {
276 return NULL;
278 return logstring;
281 static char *tracker_to_unix_path(TALLOC_CTX *mem_ctx, const char *uri)
283 GFile *f;
284 char *path;
285 char *talloc_path;
287 f = g_file_new_for_uri(uri);
288 if (f == NULL) {
289 return NULL;
292 path = g_file_get_path(f);
293 g_object_unref(f);
295 if (path == NULL) {
296 return NULL;
299 talloc_path = talloc_strdup(mem_ctx, path);
300 g_free(path);
301 if (talloc_path == NULL) {
302 return NULL;
305 return talloc_path;
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,
318 const char *path,
319 const struct stat_ex *sp)
321 sl_array_t *meta;
322 sl_nil_t nil;
323 int i, metacount, result;
324 uint64_t uint64var;
325 sl_time_t sl_time;
326 char *p;
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);
332 if (result != 0) {
333 return false;
335 return true;
338 meta = dalloc_zero(fm_array, sl_array_t);
339 if (meta == NULL) {
340 return false;
343 for (i = 0; i < metacount; i++) {
344 attribute = dalloc_get_object(reqinfo, i);
345 if (attribute == NULL) {
346 return false;
348 if (strcmp(attribute, "kMDItemDisplayName") == 0
349 || strcmp(attribute, "kMDItemFSName") == 0) {
350 p = strrchr(path, '/');
351 if (p) {
352 result = dalloc_stradd(meta, p + 1);
353 if (result != 0) {
354 return false;
357 } else if (strcmp(attribute, "kMDItemPath") == 0) {
358 result = dalloc_stradd(meta, path);
359 if (result != 0) {
360 return false;
362 } else if (strcmp(attribute, "kMDItemFSSize") == 0) {
363 uint64var = sp->st_ex_size;
364 result = dalloc_add_copy(meta, &uint64var, uint64_t);
365 if (result != 0) {
366 return false;
368 } else if (strcmp(attribute, "kMDItemFSOwnerUserID") == 0) {
369 uint64var = sp->st_ex_uid;
370 result = dalloc_add_copy(meta, &uint64var, uint64_t);
371 if (result != 0) {
372 return false;
374 } else if (strcmp(attribute, "kMDItemFSOwnerGroupID") == 0) {
375 uint64var = sp->st_ex_gid;
376 result = dalloc_add_copy(meta, &uint64var, uint64_t);
377 if (result != 0) {
378 return false;
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);
383 if (result != 0) {
384 return false;
386 } else {
387 result = dalloc_add_copy(meta, &nil, sl_nil_t);
388 if (result != 0) {
389 return false;
394 result = dalloc_add(fm_array, meta, sl_array_t);
395 if (result != 0) {
396 return false;
398 return true;
401 static int cnid_comp_fn(const void *p1, const void *p2)
403 const uint64_t *cnid1 = p1, *cnid2 = p2;
404 if (*cnid1 == *cnid2) {
405 return 0;
407 if (*cnid1 < *cnid2) {
408 return -1;
410 return 1;
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;
419 int i;
420 const void *p;
422 cnids = talloc_array(slq, uint64_t, dalloc_size(d));
423 if (cnids == NULL) {
424 return false;
427 for (i = 0; i < dalloc_size(d); i++) {
428 p = dalloc_get_object(d, i);
429 if (p == NULL) {
430 return NULL;
432 memcpy(&cnids[i], p, sizeof(uint64_t));
434 qsort(cnids, dalloc_size(d), sizeof(uint64_t), cnid_comp_fn);
436 slq->cnids = cnids;
437 slq->cnids_num = dalloc_size(d);
439 return true;
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)
448 sl_nil_t nil = 0;
449 struct sl_rslts *query_results;
450 int result;
452 if (slq->query_results) {
453 DEBUG(1, ("unexpected existing result handle\n"));
454 return false;
457 query_results = talloc_zero(slq, struct sl_rslts);
458 if (query_results == NULL) {
459 return false;
462 /* CNIDs */
463 query_results->cnids = talloc_zero(query_results, sl_cnids_t);
464 if (query_results->cnids == NULL) {
465 return false;
467 query_results->cnids->ca_cnids = dalloc_new(query_results->cnids);
468 if (query_results->cnids->ca_cnids == NULL) {
469 return false;
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));
475 return false;
477 query_results->cnids->ca_context = (uint32_t)slq->ctx2;
479 /* FileMeta */
480 query_results->fm_array = dalloc_zero(query_results, sl_array_t);
481 if (query_results->fm_array == NULL) {
482 return false;
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);
487 if (result != 0) {
488 return false;
491 slq->query_results = query_results;
492 return true;
495 static bool add_results(sl_array_t *array, struct sl_query *slq)
497 sl_filemeta_t *fm;
498 uint64_t status = 0;
499 int result;
500 bool ok;
502 /* FileMeta */
503 fm = dalloc_zero(array, sl_filemeta_t);
504 if (fm == NULL) {
505 return false;
508 result = dalloc_add_copy(array, &status, uint64_t);
509 if (result != 0) {
510 return false;
512 result = dalloc_add(array, slq->query_results->cnids, sl_cnids_t);
513 if (result != 0) {
514 return false;
516 if (slq->query_results->num_results > 0) {
517 result = dalloc_add(fm, slq->query_results->fm_array, sl_array_t);
518 if (result != 0) {
519 return false;
522 result = dalloc_add(array, fm, sl_filemeta_t);
523 if (result != 0) {
524 return false;
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);
531 if (!ok) {
532 DEBUG(1, ("couldn't add result handle\n"));
533 slq->state = SLQ_STATE_ERROR;
534 return false;
537 return true;
540 static const struct slrpc_cmd *slrpc_cmd_by_name(const char *rpccmd)
542 size_t i;
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++) {
555 int cmp;
557 cmp = strcmp(cmds[i].name, rpccmd);
558 if (cmp == 0) {
559 return &cmds[i];
563 return NULL;
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)
572 struct sl_query *q;
574 for (q = mds_ctx->query_list; q; q = q->next) {
575 if ((q->ctx1 == ctx1) && (q->ctx2 == ctx2)) {
576 return q;
580 return NULL;
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);
593 slq->mds_ctx = NULL;
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;
607 return 0;
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
616 * the db.
618 static int ino_path_map_destr_cb(struct sl_inode_path_map *entry)
620 NTSTATUS status;
621 TDB_DATA key;
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)));
628 return -1;
631 DEBUG(10,("deleted: %s\n", entry->path));
632 return 0;
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)
649 NTSTATUS status;
650 struct sl_inode_path_map *entry;
651 TDB_DATA key, value;
652 void *p;
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
661 * in the db.
664 if (value.dsize != sizeof(void *)) {
665 DEBUG(1, ("invalide dsize\n"));
666 return false;
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);
674 if (entry == NULL) {
675 DEBUG(1, ("talloc_reference failed\n"));
676 return false;
678 return true;
681 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
682 DEBUG(1, ("dbwrap_fetch failed %s\n", nt_errstr(status)));
683 return false;
686 entry = talloc_zero(slq->entries_ctx, struct sl_inode_path_map);
687 if (entry == NULL) {
688 DEBUG(1, ("talloc failed\n"));
689 return false;
692 entry->ino = ino;
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"));
697 TALLOC_FREE(entry);
698 return false;
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)));
705 TALLOC_FREE(entry);
706 return false;
709 talloc_set_destructor(entry, ino_path_map_destr_cb);
711 return true;
714 /************************************************
715 * Tracker async callbacks
716 ************************************************/
718 static void tracker_con_cb(GObject *object,
719 GAsyncResult *res,
720 gpointer user_data)
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,
726 &error);
727 if (error) {
728 DEBUG(1, ("Could not connect to Tracker: %s\n",
729 error->message));
730 g_error_free(error);
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,
740 GAsyncResult *res,
741 gpointer user_data)
743 GError *error = NULL;
744 struct sl_query *slq = talloc_get_type_abort(user_data, struct sl_query);
745 gboolean more_results;
746 const gchar *uri;
747 char *path;
748 int result;
749 struct stat_ex sb;
750 uint64_t ino64;
751 bool ok;
752 struct tevent_req *req;
754 SLQ_DEBUG(10, slq, "tracker_cursor_cb");
756 more_results = tracker_sparql_cursor_next_finish(slq->tracker_cursor,
757 res,
758 &error);
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
770 * we return.
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);
776 if (req == NULL) {
777 slq->state = SLQ_STATE_ERROR;
778 return;
780 tevent_req_set_callback(req, tracker_cursor_cb_destroy_done, NULL);
781 return;
784 if (error) {
785 DEBUG(1, ("Tracker cursor: %s\n", error->message));
786 g_error_free(error);
787 slq->state = SLQ_STATE_ERROR;
788 g_main_loop_quit(slq->mds_ctx->gmainloop);
789 return;
792 if (!more_results) {
793 slq->state = SLQ_STATE_DONE;
794 g_main_loop_quit(slq->mds_ctx->gmainloop);
795 return;
798 uri = tracker_sparql_cursor_get_string(slq->tracker_cursor, 0, NULL);
799 if (uri == NULL) {
800 DEBUG(1, ("error fetching Tracker URI\n"));
801 slq->state = SLQ_STATE_ERROR;
802 g_main_loop_quit(slq->mds_ctx->gmainloop);
803 return;
805 path = tracker_to_unix_path(slq->query_results, uri);
806 if (path == NULL) {
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);
810 return;
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);
819 if (result != 0) {
820 goto done;
822 result = access(path, R_OK);
823 if (result != 0) {
824 goto done;
827 ino64 = sb.st_ex_ino;
828 if (slq->cnids) {
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);
836 if (!ok) {
837 goto done;
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,
846 &ino64, uint64_t);
847 if (result != 0) {
848 DEBUG(1, ("dalloc error\n"));
849 slq->state = SLQ_STATE_ERROR;
850 g_main_loop_quit(slq->mds_ctx->gmainloop);
851 return;
853 ok = add_filemeta(slq->reqinfo, slq->query_results->fm_array,
854 path, &sb);
855 if (!ok) {
856 DEBUG(1, ("add_filemeta error\n"));
857 slq->state = SLQ_STATE_ERROR;
858 g_main_loop_quit(slq->mds_ctx->gmainloop);
859 return;
862 ok = inode_map_add(slq, ino64, path);
863 if (!ok) {
864 DEBUG(1, ("inode_map_add error\n"));
865 slq->state = SLQ_STATE_ERROR;
866 g_main_loop_quit(slq->mds_ctx->gmainloop);
867 return;
870 slq->query_results->num_results++;
872 done:
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);
877 return;
880 slq->state = SLQ_STATE_RESULTS;
881 SLQ_DEBUG(10, slq, "cursor next");
882 tracker_sparql_cursor_next_async(slq->tracker_cursor,
883 slq->gcancellable,
884 tracker_cursor_cb,
885 slq);
888 static void tracker_cursor_cb_destroy_done(struct tevent_req *req)
890 slq_destroy_recv(req);
891 TALLOC_FREE(req);
893 DEBUG(10, ("%s\n", __func__));
896 static void tracker_query_cb(GObject *object,
897 GAsyncResult *res,
898 gpointer user_data)
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),
907 res,
908 &error);
909 if (error) {
910 slq->state = SLQ_STATE_ERROR;
911 DEBUG(1, ("Tracker query error: %s\n", error->message));
912 g_error_free(error);
913 g_main_loop_quit(slq->mds_ctx->gmainloop);
914 return;
917 if (slq->state == SLQ_STATE_DONE) {
918 SLQ_DEBUG(10, slq, "done");
919 g_main_loop_quit(slq->mds_ctx->gmainloop);
920 talloc_free(slq);
921 return;
924 slq->state = SLQ_STATE_RESULTS;
926 tracker_sparql_cursor_next_async(slq->tracker_cursor,
927 slq->gcancellable,
928 tracker_cursor_cb,
929 slq);
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)
939 sl_dict_t *dict;
940 sl_array_t *array;
941 char *s;
942 uint64_t u;
943 sl_bool_t b;
944 sl_uuid_t uuid;
945 int result;
947 dict = dalloc_zero(reply, sl_dict_t);
948 if (dict == NULL) {
949 return false;
952 /* kMDSStoreHasPersistentUUID = false */
953 result = dalloc_stradd(dict, "kMDSStoreHasPersistentUUID");
954 if (result != 0) {
955 return false;
957 b = false;
958 result = dalloc_add_copy(dict, &b, sl_bool_t);
959 if (result != 0) {
960 return false;
963 /* kMDSStoreIsBackup = false */
964 result = dalloc_stradd(dict, "kMDSStoreIsBackup");
965 if (result != 0) {
966 return false;
968 b = false;
969 result = dalloc_add_copy(dict, &b, sl_bool_t);
970 if (result != 0) {
971 return false;
974 /* kMDSStoreUUID = uuid */
975 result = dalloc_stradd(dict, "kMDSStoreUUID");
976 if (result != 0) {
977 return false;
979 memcpy(uuid.sl_uuid, "fakeuuidfakeuuid", sizeof(uuid.sl_uuid));
980 result = dalloc_add_copy(dict, &uuid, sl_uuid_t);
981 if (result != 0) {
982 return false;
985 /* kMDSStoreSupportsVolFS = true */
986 result = dalloc_stradd(dict, "kMDSStoreSupportsVolFS");
987 if (result != 0) {
988 return false;
990 b = true;
991 result = dalloc_add_copy(dict, &b, sl_bool_t);
992 if (result != 0) {
993 return false;
996 /* kMDSVolumeUUID = uuid */
997 result = dalloc_stradd(dict, "kMDSVolumeUUID");
998 if (result != 0) {
999 return false;
1001 memcpy(uuid.sl_uuid, "fakeuuidfakeuuid", sizeof(uuid.sl_uuid));
1002 result = dalloc_add_copy(dict, &uuid, sl_uuid_t);
1003 if (result != 0) {
1004 return false;
1007 /* kMDSDiskStoreSpindleNumber = 1 (fake) */
1008 result = dalloc_stradd(dict, "kMDSDiskStoreSpindleNumber");
1009 if (result != 0) {
1010 return false;
1012 u = 1;
1013 result = dalloc_add_copy(dict, &u, uint64_t);
1014 if (result != 0) {
1015 return false;
1018 /* kMDSDiskStorePolicy = 3 (whatever that means, taken from OS X) */
1019 result = dalloc_stradd(dict, "kMDSDiskStorePolicy");
1020 if (result != 0) {
1021 return false;
1023 u = 3;
1024 result = dalloc_add_copy(dict, &u, uint64_t);
1025 if (result != 0) {
1026 return false;
1029 /* kMDSStoreMetaScopes array */
1030 array = dalloc_zero(dict, sl_array_t);
1031 if (array == NULL) {
1032 return NULL;
1034 result = dalloc_stradd(array, "kMDQueryScopeComputer");
1035 if (result != 0) {
1036 return false;
1038 result = dalloc_stradd(array, "kMDQueryScopeAllIndexed");
1039 if (result != 0) {
1040 return false;
1042 result = dalloc_stradd(array, "kMDQueryScopeComputerIndexed");
1043 if (result != 0) {
1044 return false;
1046 result = dalloc_add(dict, array, sl_array_t);
1047 if (result != 0) {
1048 return false;
1051 /* kMDSStoreDevice = 0x1000003 (whatever that means, taken from OS X) */
1052 result = dalloc_stradd(dict, "kMDSStoreDevice");
1053 if (result != 0) {
1054 return false;
1056 u = 0x1000003;
1057 result = dalloc_add_copy(dict, &u, uint64_t);
1058 if (result != 0) {
1059 return false;
1062 /* kMDSStoreSupportsTCC = true (whatever that means, taken from OS X) */
1063 result = dalloc_stradd(dict, "kMDSStoreSupportsTCC");
1064 if (result != 0) {
1065 return false;
1067 b = true;
1068 result = dalloc_add_copy(dict, &b, sl_bool_t);
1069 if (result != 0) {
1070 return false;
1073 /* kMDSStorePathScopes = ["/"] (whatever that means, taken from OS X) */
1074 result = dalloc_stradd(dict, "kMDSStorePathScopes");
1075 if (result != 0) {
1076 return false;
1078 array = dalloc_zero(dict, sl_array_t);
1079 if (array == NULL) {
1080 return false;
1082 s = talloc_strdup(dict, "/");
1083 if (s == NULL) {
1084 return false;
1086 talloc_set_name(s, "smb_ucs2_t *");
1087 result = dalloc_add(array, s, smb_ucs2_t *);
1088 if (result != 0) {
1089 return false;
1091 result = dalloc_add(dict, array, sl_array_t);
1092 if (result != 0) {
1093 return false;
1096 result = dalloc_add(reply, dict, sl_dict_t);
1097 if (result != 0) {
1098 return false;
1101 return true;
1104 static void slq_close_timer(struct tevent_context *ev,
1105 struct tevent_timer *te,
1106 struct timeval current_time,
1107 void *private_data)
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");
1115 TALLOC_FREE(slq);
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)
1130 bool ok;
1131 uint64_t sl_result;
1132 uint64_t *uint64p;
1133 DALLOC_CTX *reqinfo;
1134 sl_array_t *array, *path_scope;
1135 sl_cnids_t *cnids;
1136 struct sl_query *slq = NULL;
1137 int result;
1138 char *querystring;
1140 array = dalloc_zero(reply, sl_array_t);
1141 if (array == NULL) {
1142 return false;
1145 if (mds_ctx->tracker_con == NULL) {
1146 DEBUG(1, ("no connection to Tracker\n"));
1147 goto error;
1150 /* Allocate and initialize query object */
1151 slq = talloc_zero(mds_ctx, struct sl_query);
1152 if (slq == NULL) {
1153 return false;
1155 slq->entries_ctx = talloc_named_const(slq, 0, "struct sl_query.entries_ctx");
1156 if (slq->entries_ctx == NULL) {
1157 TALLOC_FREE(slq);
1158 return false;
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"));
1171 goto error;
1174 slq->gcancellable = g_cancellable_new();
1175 if (slq->gcancellable == NULL) {
1176 DEBUG(1,("error from g_cancellable_new\n"));
1177 goto error;
1180 querystring = dalloc_value_for_key(query, "DALLOC_CTX", 0,
1181 "DALLOC_CTX", 1,
1182 "kMDQueryString");
1183 if (querystring == NULL) {
1184 DEBUG(1, ("missing kMDQueryString\n"));
1185 goto error;
1187 slq->query_string = talloc_strdup(slq, querystring);
1188 if (slq->query_string == NULL) {
1189 DEBUG(1, ("out of memory\n"));
1190 goto error;
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,
1199 "uint64_t", 1);
1200 if (uint64p == NULL) {
1201 goto error;
1203 slq->ctx1 = *uint64p;
1204 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1205 "uint64_t", 2);
1206 if (uint64p == NULL) {
1207 goto error;
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) {
1214 goto error;
1217 slq->path_scope = dalloc_get(path_scope, "char *", 0);
1218 if (slq->path_scope == NULL) {
1219 goto error;
1222 slq->path_scope = talloc_strdup(slq, slq->path_scope);
1223 if (slq->path_scope == NULL) {
1224 goto error;
1228 reqinfo = dalloc_value_for_key(query, "DALLOC_CTX", 0,
1229 "DALLOC_CTX", 1, "kMDAttributeArray");
1230 if (reqinfo == NULL) {
1231 goto error;
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");
1239 if (cnids) {
1240 ok = sort_cnids(slq, cnids->ca_cnids);
1241 if (!ok) {
1242 goto error;
1246 ok = create_result_handle(slq);
1247 if (!ok) {
1248 DEBUG(1, ("create_result_handle error\n"));
1249 slq->state = SLQ_STATE_ERROR;
1250 goto error;
1253 SLQ_DEBUG(10, slq, "new");
1255 DLIST_ADD(mds_ctx->query_list, slq);
1257 ok = map_spotlight_to_sparql_query(slq);
1258 if (!ok) {
1260 * Two cases:
1262 * 1) the query string is "false", the parser returns
1263 * an error for that. We're supposed to return -1
1264 * here.
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");
1271 goto error;
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,
1278 slq->sparql_query,
1279 slq->gcancellable,
1280 tracker_query_cb,
1281 slq);
1282 g_main_context_pop_thread_default(mds_ctx->gcontext);
1283 slq->state = SLQ_STATE_RUNNING;
1285 sl_result = 0;
1286 result = dalloc_add_copy(array, &sl_result, uint64_t);
1287 if (result != 0) {
1288 goto error;
1290 result = dalloc_add(reply, array, sl_array_t);
1291 if (result != 0) {
1292 goto error;
1294 return true;
1296 error:
1297 sl_result = UINT64_MAX;
1298 TALLOC_FREE(slq);
1299 result = dalloc_add_copy(array, &sl_result, uint64_t);
1300 if (result != 0) {
1301 return false;
1303 result = dalloc_add(reply, array, sl_array_t);
1304 if (result != 0) {
1305 return false;
1307 return true;
1311 * Fetch results of a query
1313 static bool slrpc_fetch_query_results(struct mds_ctx *mds_ctx,
1314 const DALLOC_CTX *query,
1315 DALLOC_CTX *reply)
1317 bool ok;
1318 struct sl_query *slq = NULL;
1319 uint64_t *uint64p, ctx1, ctx2;
1320 uint64_t status;
1321 sl_array_t *array;
1322 int result;
1324 array = dalloc_zero(reply, sl_array_t);
1325 if (array == NULL) {
1326 return false;
1329 /* Get query for context */
1330 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1331 "uint64_t", 1);
1332 if (uint64p == NULL) {
1333 goto error;
1335 ctx1 = *uint64p;
1337 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1338 "uint64_t", 2);
1339 if (uint64p == NULL) {
1340 goto error;
1342 ctx2 = *uint64p;
1344 slq = slq_for_ctx(mds_ctx, ctx1, ctx2);
1345 if (slq == NULL) {
1346 DEBUG(1, ("bad context: [0x%jx,0x%jx]\n",
1347 (uintmax_t)ctx1, (uintmax_t)ctx2));
1348 goto error;
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"));
1358 goto error;
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);
1369 if (!ok) {
1370 DEBUG(1, ("error adding results\n"));
1371 goto error;
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,
1378 slq->gcancellable,
1379 tracker_cursor_cb,
1380 slq);
1381 g_main_context_pop_thread_default(mds_ctx->gcontext);
1383 break;
1385 case SLQ_STATE_ERROR:
1386 DEBUG(1, ("query in error state\n"));
1387 goto error;
1389 default:
1390 DEBUG(1, ("unexpected query state %d\n", slq->state));
1391 goto error;
1394 result = dalloc_add(reply, array, sl_array_t);
1395 if (result != 0) {
1396 goto error;
1398 return true;
1400 error:
1401 status = UINT64_MAX;
1402 TALLOC_FREE(slq);
1403 result = dalloc_add_copy(array, &status, uint64_t);
1404 if (result != 0) {
1405 return false;
1407 result = dalloc_add(reply, array, sl_array_t);
1408 if (result != 0) {
1409 return false;
1411 return true;
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)
1420 uint64_t sl_result;
1421 sl_array_t *array;
1422 int result;
1424 array = dalloc_zero(reply, sl_array_t);
1425 if (array == NULL) {
1426 return false;
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
1433 * to refresh view.
1436 sl_result = 0;
1437 result = dalloc_add_copy(array, &sl_result, uint64_t);
1438 if (result != 0) {
1439 return false;
1441 result = dalloc_add(reply, array, sl_array_t);
1442 if (result != 0) {
1443 return false;
1446 return true;
1450 * Fetch supported metadata attributes for a CNID
1452 static bool slrpc_fetch_attributenames(struct mds_ctx *mds_ctx,
1453 const DALLOC_CTX *query,
1454 DALLOC_CTX *reply)
1456 uint64_t id;
1457 sl_cnids_t *cnids;
1458 sl_array_t *array;
1459 uint64_t sl_result;
1460 sl_cnids_t *replycnids;
1461 sl_array_t *mdattrs;
1462 sl_filemeta_t *fmeta;
1463 int result;
1464 void *p;
1466 cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 1);
1467 if (cnids == NULL) {
1468 return false;
1471 p = dalloc_get_object(cnids->ca_cnids, 0);
1472 if (p == NULL) {
1473 return NULL;
1475 memcpy(&id, p, sizeof(uint64_t));
1477 /* Result array */
1478 array = dalloc_zero(reply, sl_array_t);
1479 if (array == NULL) {
1480 return false;
1483 result = dalloc_add(reply, array, sl_array_t);
1484 if (result != 0) {
1485 return false;
1488 /* Return result value 0 */
1489 sl_result = 0;
1490 result = dalloc_add_copy(array, &sl_result, uint64_t);
1491 if (result != 0) {
1492 return false;
1495 /* Return CNID array */
1496 replycnids = talloc_zero(reply, sl_cnids_t);
1497 if (replycnids == NULL) {
1498 return false;
1501 replycnids->ca_cnids = dalloc_new(cnids);
1502 if (replycnids->ca_cnids == NULL) {
1503 return false;
1506 replycnids->ca_unkn1 = 0xfec;
1507 replycnids->ca_context = cnids->ca_context;
1508 result = dalloc_add_copy(replycnids->ca_cnids, &id, uint64_t);
1509 if (result != 0) {
1510 return false;
1512 result = dalloc_add(array, replycnids, sl_cnids_t);
1513 if (result != 0) {
1514 return false;
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) {
1523 return false;
1526 result = dalloc_stradd(mdattrs, "kMDItemFSName");
1527 if (result != 0) {
1528 return false;
1530 result = dalloc_stradd(mdattrs, "kMDItemDisplayName");
1531 if (result != 0) {
1532 return false;
1534 result = dalloc_stradd(mdattrs, "kMDItemFSSize");
1535 if (result != 0) {
1536 return false;
1538 result = dalloc_stradd(mdattrs, "kMDItemFSOwnerUserID");
1539 if (result != 0) {
1540 return false;
1542 result = dalloc_stradd(mdattrs, "kMDItemFSOwnerGroupID");
1543 if (result != 0) {
1544 return false;
1546 result = dalloc_stradd(mdattrs, "kMDItemFSContentChangeDate");
1547 if (result != 0) {
1548 return false;
1551 fmeta = dalloc_zero(reply, sl_filemeta_t);
1552 if (fmeta == NULL) {
1553 return false;
1555 result = dalloc_add(fmeta, mdattrs, sl_array_t);
1556 if (result != 0) {
1557 return false;
1559 result = dalloc_add(array, fmeta, sl_filemeta_t);
1560 if (result != 0) {
1561 return false;
1564 return true;
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)
1573 int result;
1574 bool ok;
1575 sl_array_t *array;
1576 sl_cnids_t *cnids;
1577 sl_cnids_t *replycnids;
1578 sl_array_t *reqinfo;
1579 uint64_t ino;
1580 uint64_t sl_result;
1581 sl_filemeta_t *fm;
1582 sl_array_t *fm_array;
1583 sl_nil_t nil;
1584 struct stat_ex sb;
1585 struct sl_inode_path_map *elem = NULL;
1586 void *p;
1587 TDB_DATA val = tdb_null;
1588 NTSTATUS status;
1590 array = dalloc_zero(reply, sl_array_t);
1591 if (array == NULL) {
1592 return false;
1594 replycnids = talloc_zero(reply, sl_cnids_t);
1595 if (replycnids == NULL) {
1596 goto error;
1598 replycnids->ca_cnids = dalloc_new(replycnids);
1599 if (replycnids->ca_cnids == NULL) {
1600 goto error;
1602 fm = dalloc_zero(array, sl_filemeta_t);
1603 if (fm == NULL) {
1604 goto error;
1606 fm_array = dalloc_zero(fm, sl_array_t);
1607 if (fm_array == NULL) {
1608 goto error;
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);
1612 if (result == -1) {
1613 goto error;
1616 reqinfo = dalloc_get(query, "DALLOC_CTX", 0, "sl_array_t", 1);
1617 if (reqinfo == NULL) {
1618 goto error;
1621 cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 2);
1622 if (cnids == NULL) {
1623 goto error;
1625 p = dalloc_get_object(cnids->ca_cnids, 0);
1626 if (p == NULL) {
1627 goto error;
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);
1634 if (result != 0) {
1635 goto error;
1638 status = dbwrap_fetch(mds_ctx->ino_path_map, reply,
1639 make_tdb_data((void*)&ino, sizeof(uint64_t)),
1640 &val);
1641 if (!NT_STATUS_IS_OK(status)) {
1642 DEBUG(1, ("Failed to fetch inode: %s\n", nt_errstr(status)));
1643 goto error;
1645 if (val.dsize != sizeof(p)) {
1646 DEBUG(1, ("invalid record pointer size: %zd\n", val.dsize));
1647 TALLOC_FREE(val.dptr);
1648 goto error;
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);
1655 if (result != 0) {
1656 goto error;
1659 ok = add_filemeta(reqinfo, fm_array, elem->path, &sb);
1660 if (!ok) {
1661 goto error;
1664 sl_result = 0;
1665 result = dalloc_add_copy(array, &sl_result, uint64_t);
1666 if (result != 0) {
1667 goto error;
1669 result = dalloc_add(array, replycnids, sl_cnids_t);
1670 if (result != 0) {
1671 goto error;
1673 result = dalloc_add(fm, fm_array, sl_array_t);
1674 if (result != 0) {
1675 goto error;
1677 result = dalloc_add(array, fm, sl_filemeta_t);
1678 if (result != 0) {
1679 goto error;
1681 result = dalloc_add(reply, array, sl_array_t);
1682 if (result != 0) {
1683 goto error;
1686 return true;
1688 error:
1689 sl_result = UINT64_MAX;
1690 result = dalloc_add_copy(array, &sl_result, uint64_t);
1691 if (result != 0) {
1692 return false;
1694 result = dalloc_add(reply, array, sl_array_t);
1695 if (result != 0) {
1696 return false;
1699 return true;
1703 * Close a query
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;
1710 sl_array_t *array;
1711 uint64_t sl_res;
1712 int result;
1714 array = dalloc_zero(reply, sl_array_t);
1715 if (array == NULL) {
1716 return false;
1719 /* Context */
1720 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1721 "uint64_t", 1);
1722 if (uint64p == NULL) {
1723 goto done;
1725 ctx1 = *uint64p;
1727 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1728 "uint64_t", 2);
1729 if (uint64p == NULL) {
1730 goto done;
1732 ctx2 = *uint64p;
1734 /* Get query for context and free it */
1735 slq = slq_for_ctx(mds_ctx, ctx1, ctx2);
1736 if (slq == NULL) {
1737 DEBUG(1, ("bad context: [0x%jx,0x%jx]\n",
1738 (uintmax_t)ctx1, (uintmax_t)ctx2));
1739 goto done;
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
1750 * tevent.
1752 slq->state = SLQ_STATE_DONE;
1753 break;
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.
1763 TALLOC_FREE(slq);
1764 break;
1766 default:
1767 DEBUG(1, ("close: unexpected state: %d\n", slq->state));
1768 break;
1772 done:
1773 sl_res = 0;
1774 result = dalloc_add_copy(array, &sl_res, uint64_t);
1775 if (result != 0) {
1776 return false;
1778 result = dalloc_add(reply, array, sl_array_t);
1779 if (result != 0) {
1780 return false;
1782 return true;
1786 * Init callbacks at startup, nothing to do here really
1788 bool mds_init(struct messaging_context *msg_ctx)
1790 return true;
1793 bool mds_shutdown(void)
1795 return true;
1798 static gboolean gmainloop_timer(gpointer user_data)
1800 struct mds_ctx *ctx = talloc_get_type_abort(user_data, struct mds_ctx);
1802 DEBUG(10,("%s\n", __func__));
1803 g_main_loop_quit(ctx->gmainloop);
1805 return G_SOURCE_CONTINUE;
1809 * Initialise a context per share handle
1811 struct mds_ctx *mds_init_ctx(TALLOC_CTX *mem_ctx,
1812 const struct auth_session_info *session_info,
1813 const char *path)
1815 struct mds_ctx *mds_ctx;
1817 mds_ctx = talloc_zero(mem_ctx, struct mds_ctx);
1818 if (mds_ctx == NULL) {
1819 return NULL;
1821 talloc_set_destructor(mds_ctx, mds_ctx_destructor_cb);
1823 mds_ctx->spath = talloc_strdup(mds_ctx, path);
1824 if (mds_ctx->spath == NULL) {
1825 goto error;
1828 if (session_info->security_token->num_sids < 1) {
1829 goto error;
1831 sid_copy(&mds_ctx->sid, &session_info->security_token->sids[0]);
1832 mds_ctx->uid = session_info->unix_token->uid;
1834 mds_ctx->ino_path_map = db_open_rbt(mds_ctx);
1835 if (mds_ctx->ino_path_map == NULL) {
1836 DEBUG(1,("open inode map db failed\n"));
1837 goto error;
1840 mds_ctx->gcontext = g_main_context_new();
1841 if (mds_ctx->gcontext == NULL) {
1842 DEBUG(1,("error from g_main_context_new\n"));
1843 goto error;
1846 mds_ctx->gmainloop = g_main_loop_new(mds_ctx->gcontext, false);
1847 if (mds_ctx->gmainloop == NULL) {
1848 DEBUG(1,("error from g_main_loop_new\n"));
1849 goto error;
1852 g_main_context_push_thread_default(mds_ctx->gcontext);
1853 tracker_sparql_connection_get_async(mds_ctx->gcancellable,
1854 tracker_con_cb, mds_ctx);
1855 g_main_context_pop_thread_default(mds_ctx->gcontext);
1857 return mds_ctx;
1859 error:
1860 TALLOC_FREE(mds_ctx);
1861 return NULL;
1865 * Tear down connections and free all resources
1867 int mds_ctx_destructor_cb(struct mds_ctx *mds_ctx)
1870 * We need to free query_list before ino_path_map
1872 while (mds_ctx->query_list != NULL) {
1874 * slq destructor removes element from list.
1875 * Don't use TALLOC_FREE()!
1877 talloc_free(mds_ctx->query_list);
1879 TALLOC_FREE(mds_ctx->ino_path_map);
1881 if (mds_ctx->tracker_con != NULL) {
1882 g_object_unref(mds_ctx->tracker_con);
1884 if (mds_ctx->gcancellable != NULL) {
1885 g_cancellable_cancel(mds_ctx->gcancellable);
1886 g_object_unref(mds_ctx->gcancellable);
1888 if (mds_ctx->gmainloop != NULL) {
1889 g_main_loop_unref(mds_ctx->gmainloop);
1891 if (mds_ctx->gcontext != NULL) {
1892 g_main_context_unref(mds_ctx->gcontext);
1895 ZERO_STRUCTP(mds_ctx);
1897 return 0;
1900 static bool mds_run_gmainloop(struct mds_ctx *mds_ctx, guint timeout)
1902 guint timer_id;
1903 GSource *timer;
1906 * It seems the event processing of the libtracker-sparql
1907 * async subsystem defers callbacks until *all* events are
1908 * processes by the async subsystem main processing loop.
1910 * g_main_context_iteration(may_block=FALSE) can't be used,
1911 * because a search that produces a few thousand matches
1912 * generates as many events that must be processed in either
1913 * g_main_context_iteration() or g_main_loop_run() before
1914 * callbacks are called.
1916 * Unfortunately g_main_context_iteration() only processes a
1917 * small subset of these event (1-30) at a time when run in
1918 * mds_dispatch(), which happens once a second while the
1919 * client polls for results.
1921 * Carefully using the blocking g_main_loop_run() fixes
1922 * this. It processes events until we exit from the loop at
1923 * defined exit points. By adding a 1 ms timeout we at least
1924 * try to get as close as possible to non-blocking behaviour.
1927 if (!g_main_context_pending(mds_ctx->gcontext)) {
1928 return true;
1931 g_main_context_push_thread_default(mds_ctx->gcontext);
1933 timer = g_timeout_source_new(timeout);
1934 if (timer == NULL) {
1935 DEBUG(1,("g_timeout_source_new_seconds\n"));
1936 g_main_context_pop_thread_default(mds_ctx->gcontext);
1937 return false;
1940 timer_id = g_source_attach(timer, mds_ctx->gcontext);
1941 if (timer_id == 0) {
1942 DEBUG(1,("g_timeout_add failed\n"));
1943 g_source_destroy(timer);
1944 g_main_context_pop_thread_default(mds_ctx->gcontext);
1945 return false;
1948 g_source_set_callback(timer, gmainloop_timer, mds_ctx, NULL);
1950 g_main_loop_run(mds_ctx->gmainloop);
1952 g_source_destroy(timer);
1954 g_main_context_pop_thread_default(mds_ctx->gcontext);
1955 return true;
1959 * Dispatch a Spotlight RPC command
1961 bool mds_dispatch(struct mds_ctx *mds_ctx,
1962 struct mdssvc_blob *request_blob,
1963 struct mdssvc_blob *response_blob)
1965 bool ok;
1966 ssize_t len;
1967 DALLOC_CTX *query = NULL;
1968 DALLOC_CTX *reply = NULL;
1969 char *rpccmd;
1970 const struct slrpc_cmd *slcmd;
1972 if (CHECK_DEBUGLVL(10)) {
1973 const struct sl_query *slq;
1975 for (slq = mds_ctx->query_list; slq != NULL; slq = slq->next) {
1976 SLQ_DEBUG(10, slq, "pending");
1980 response_blob->length = 0;
1983 * Process finished glib events.
1985 * FIXME: integrate with tevent instead of piggy packing it
1986 * onto the processing of new requests.
1988 * mds_dispatch() is called by the client a few times in a row:
1990 * - first in order to open/start a search query
1992 * - later in order to fetch results asynchronously, typically
1993 * once a second. If no results have been retrieved from the
1994 * search store (Tracker) yet, we return no results.
1995 * The client asks for more results every second as long
1996 * as the "Search Window" in the client gui is open.
1998 * - at some point the query is closed
2000 * This means we try to iterate through the glib event loop
2001 * before processing the request in order to get result
2002 * from tracker which can be returned to the client.
2005 ok = mds_run_gmainloop(mds_ctx, MDS_TRACKER_ASYNC_TIMEOUT_MS);
2006 if (!ok) {
2007 goto cleanup;
2010 DEBUG(10, ("share path: %s\n", mds_ctx->spath));
2012 query = dalloc_new(mds_ctx);
2013 if (query == NULL) {
2014 ok = false;
2015 goto cleanup;
2017 reply = dalloc_new(mds_ctx);
2018 if (reply == NULL) {
2019 ok = false;
2020 goto cleanup;
2023 ok = sl_unpack(query, (char *)request_blob->spotlight_blob,
2024 request_blob->length);
2025 if (!ok) {
2026 DEBUG(1, ("error unpacking Spotlight RPC blob\n"));
2027 goto cleanup;
2030 DEBUG(5, ("%s", mds_dalloc_dump(query, 0)));
2032 rpccmd = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
2033 "char *", 0);
2034 if (rpccmd == NULL) {
2035 DEBUG(1, ("missing primary Spotlight RPC command\n"));
2036 ok = false;
2037 goto cleanup;
2040 DEBUG(10, ("Spotlight RPC cmd: %s\n", rpccmd));
2042 slcmd = slrpc_cmd_by_name(rpccmd);
2043 if (slcmd == NULL) {
2044 DEBUG(1, ("unsupported primary Spotlight RPC command %s\n",
2045 rpccmd));
2046 ok = false;
2047 goto cleanup;
2051 * If these functions return an error, they hit something like
2052 * a non recoverable talloc error
2054 ok = slcmd->function(mds_ctx, query, reply);
2055 if (!ok) {
2056 DEBUG(1, ("error in Spotlight RPC handler\n"));
2057 goto cleanup;
2060 DEBUG(5, ("%s", mds_dalloc_dump(reply, 0)));
2062 len = sl_pack(reply, (char *)response_blob->spotlight_blob,
2063 response_blob->size);
2064 if (len == -1) {
2065 DEBUG(1, ("error packing Spotlight RPC reply\n"));
2066 ok = false;
2067 goto cleanup;
2071 * Run g_main_loop a second time in order to dispatch events
2072 * that may have been queued at the libtracker-sparql level.
2073 * As we only want to dispatch (write out requests) but not
2074 * wait for anything, we use a much shorter timeout here.
2076 ok = mds_run_gmainloop(mds_ctx, MDS_TRACKER_ASYNC_TIMEOUT_MS / 10);
2077 if (!ok) {
2078 goto cleanup;
2081 response_blob->length = len;
2083 cleanup:
2084 talloc_free(query);
2085 talloc_free(reply);
2086 return ok;