s3/rpc_server/mdssvc: Generate flex/bison files in build
[samba.git] / source3 / rpc_server / mdssvc / mdssvc.c
1 /*
2    Unix SMB/CIFS implementation.
3    Main metadata server / Spotlight routines
4
5    Copyright (C) Ralph Boehme 2012-2014
6
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.
11
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.
16
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/>.
19 */
20
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 "rpc_server/mdssvc/sparql_parser.tab.h"
31
32 #undef DBGC_CLASS
33 #define DBGC_CLASS DBGC_RPC_SRV
34
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)
54
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);
60 };
61
62 struct slq_destroy_state {
63         struct tevent_context *ev;
64         struct sl_query *slq;
65 };
66
67 /*
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.
71  */
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);
86
87 static struct tevent_req *slq_destroy_send(TALLOC_CTX *mem_ctx,
88                                            struct tevent_context *ev,
89                                            struct sl_query **slq)
90 {
91         struct tevent_req *req;
92         struct slq_destroy_state *state;
93
94         req = tevent_req_create(mem_ctx, &state,
95                                 struct slq_destroy_state);
96         if (req == NULL) {
97                 return NULL;
98         }
99         state->slq = talloc_move(state, slq);
100         tevent_req_done(req);
101
102         return tevent_req_post(req, ev);
103 }
104
105 static void slq_destroy_recv(struct tevent_req *req)
106 {
107         tevent_req_received(req);
108 }
109
110 /************************************************
111  * Misc utility functions
112  ************************************************/
113
114 static char *tab_level(TALLOC_CTX *mem_ctx, int level)
115 {
116         int i;
117         char *string = talloc_array(mem_ctx, char, level + 1);
118
119         for (i = 0; i < level; i++) {
120                 string[i] = '\t';
121         }
122
123         string[i] = '\0';
124         return string;
125 }
126
127 char *mds_dalloc_dump(DALLOC_CTX *dd, int nestinglevel)
128 {
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;
143
144         tab_string1 = tab_level(dd, nestinglevel);
145         if (tab_string1 == NULL) {
146                 return NULL;
147         }
148         tab_string2 = tab_level(dd, nestinglevel + 1);
149         if (tab_string2 == NULL) {
150                 return NULL;
151         }
152
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;
160         }
161
162         for (n = 0; n < dalloc_size(dd); n++) {
163                 type = dalloc_get_name(dd, n);
164                 if (type == NULL) {
165                         return NULL;
166                 }
167                 p = dalloc_get_object(dd, n);
168                 if (p == NULL) {
169                         return NULL;
170                 }
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;
178                         }
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,
197                                                    p,
198                                                    talloc_get_size(p),
199                                                    &utf8string,
200                                                    &utf8len);
201                         if (!ok) {
202                                 return NULL;
203                         }
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;
227                         }
228                         result = strftime(datestring,
229                                          sizeof(datestring),
230                                          "%Y-%m-%d %H:%M:%S", tm);
231                         if (result == 0) {
232                                 return NULL;
233                         }
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;
250                         }
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;
257                                 }
258                                 logstring = talloc_strdup_append(logstring,
259                                                                  nested_logstring);
260                         }
261                 } else {
262                         logstring = talloc_asprintf_append(
263                                 logstring,
264                                 "%stype: %s\n",
265                                 tab_string2,
266                                 type);
267                 }
268                 if (logstring == NULL) {
269                         return NULL;
270                 }
271         }
272         logstring = talloc_asprintf_append(logstring,
273                                            "%s}\n",
274                                            tab_string1);
275         if (logstring == NULL) {
276                 return NULL;
277         }
278         return logstring;
279 }
280
281 static char *tracker_to_unix_path(TALLOC_CTX *mem_ctx, const char *uri)
282 {
283         GFile *f;
284         char *path;
285         char *talloc_path;
286
287         f = g_file_new_for_uri(uri);
288         if (f == NULL) {
289                 return NULL;
290         }
291
292         path = g_file_get_path(f);
293         g_object_unref(f);
294
295         if (path == NULL) {
296                 return NULL;
297         }
298
299         talloc_path = talloc_strdup(mem_ctx, path);
300         g_free(path);
301         if (talloc_path == NULL) {
302                 return NULL;
303         }
304
305         return talloc_path;
306 }
307
308 /**
309  * Add requested metadata for a query result element
310  *
311  * This could be rewritten to something more sophisticated like
312  * querying metadata from Tracker.
313  *
314  * If path or sp is NULL, simply add nil values for all attributes.
315  **/
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)
320 {
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;
328
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;
334                 }
335                 return true;
336         }
337
338         meta = dalloc_zero(fm_array, sl_array_t);
339         if (meta == NULL) {
340                 return false;
341         }
342
343         for (i = 0; i < metacount; i++) {
344                 attribute = dalloc_get_object(reqinfo, i);
345                 if (attribute == NULL) {
346                         return false;
347                 }
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;
355                                 }
356                         }
357                 } else if (strcmp(attribute, "kMDItemPath") == 0) {
358                         result = dalloc_stradd(meta, path);
359                         if (result != 0) {
360                                 return false;
361                         }
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;
367                         }
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;
373                         }
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;
379                         }
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;
385                         }
386                 } else {
387                         result = dalloc_add_copy(meta, &nil, sl_nil_t);
388                         if (result != 0) {
389                                 return false;
390                         }
391                 }
392         }
393
394         result = dalloc_add(fm_array, meta, sl_array_t);
395         if (result != 0) {
396                 return false;
397         }
398         return true;
399 }
400
401 static int cnid_comp_fn(const void *p1, const void *p2)
402 {
403         const uint64_t *cnid1 = p1, *cnid2 = p2;
404         if (*cnid1 == *cnid2) {
405                 return 0;
406         }
407         if (*cnid1 < *cnid2) {
408                 return -1;
409         }
410         return 1;
411 }
412
413 /**
414  * Create a sorted copy of a CNID array
415  **/
416 static bool sort_cnids(struct sl_query *slq, const DALLOC_CTX *d)
417 {
418         uint64_t *cnids = NULL;
419         int i;
420         const void *p;
421
422         cnids = talloc_array(slq, uint64_t, dalloc_size(d));
423         if (cnids == NULL) {
424                 return false;
425         }
426
427         for (i = 0; i < dalloc_size(d); i++) {
428                 p = dalloc_get_object(d, i);
429                 if (p == NULL) {
430                         return NULL;
431                 }
432                 memcpy(&cnids[i], p, sizeof(uint64_t));
433         }
434         qsort(cnids, dalloc_size(d), sizeof(uint64_t), cnid_comp_fn);
435
436         slq->cnids = cnids;
437         slq->cnids_num = dalloc_size(d);
438
439         return true;
440 }
441
442 /**
443  * Allocate result handle used in the async Tracker cursor result
444  * handler for storing results
445  **/
446 static bool create_result_handle(struct sl_query *slq)
447 {
448         sl_nil_t nil = 0;
449         struct sl_rslts *query_results;
450         int result;
451
452         if (slq->query_results) {
453                 DEBUG(1, ("unexpected existing result handle\n"));
454                 return false;
455         }
456
457         query_results = talloc_zero(slq, struct sl_rslts);
458         if (query_results == NULL) {
459                 return false;
460         }
461
462         /* CNIDs */
463         query_results->cnids = talloc_zero(query_results, sl_cnids_t);
464         if (query_results->cnids == NULL) {
465                 return false;
466         }
467         query_results->cnids->ca_cnids = dalloc_new(query_results->cnids);
468         if (query_results->cnids->ca_cnids == NULL) {
469                 return false;
470         }
471
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;
476         }
477         query_results->cnids->ca_context = (uint32_t)slq->ctx2;
478
479         /* FileMeta */
480         query_results->fm_array = dalloc_zero(query_results, sl_array_t);
481         if (query_results->fm_array == NULL) {
482                 return false;
483         }
484
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;
489         }
490
491         slq->query_results = query_results;
492         return true;
493 }
494
495 static bool add_results(sl_array_t *array, struct sl_query *slq)
496 {
497         sl_filemeta_t *fm;
498         uint64_t status = 0;
499         int result;
500         bool ok;
501
502         /* FileMeta */
503         fm = dalloc_zero(array, sl_filemeta_t);
504         if (fm == NULL) {
505                 return false;
506         }
507
508         result = dalloc_add_copy(array, &status, uint64_t);
509         if (result != 0) {
510                 return false;
511         }
512         result = dalloc_add(array, slq->query_results->cnids, sl_cnids_t);
513         if (result != 0) {
514                 return false;
515         }
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;
520                 }
521         }
522         result = dalloc_add(array, fm, sl_filemeta_t);
523         if (result != 0) {
524                 return false;
525         }
526
527         /* This ensure the results get clean up after been sent to the client */
528         talloc_move(array, &slq->query_results);
529
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;
535         }
536
537         return true;
538 }
539
540 static const struct slrpc_cmd *slrpc_cmd_by_name(const char *rpccmd)
541 {
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},
552         };
553
554         for (i = 0; i < ARRAY_SIZE(cmds); i++) {
555                 int cmp;
556
557                 cmp = strcmp(cmds[i].name, rpccmd);
558                 if (cmp == 0) {
559                         return &cmds[i];
560                 }
561         }
562
563         return NULL;
564 }
565
566 /**
567  * Search the list of active queries given their context ids
568  **/
569 static struct sl_query *slq_for_ctx(struct mds_ctx *mds_ctx,
570                                     uint64_t ctx1, uint64_t ctx2)
571 {
572         struct sl_query *q;
573
574         for (q = mds_ctx->query_list; q; q = q->next) {
575                 if ((q->ctx1 == ctx1) && (q->ctx2 == ctx2)) {
576                         return q;
577                 }
578         }
579
580         return NULL;
581 }
582
583 static int slq_destructor_cb(struct sl_query *slq)
584 {
585         SLQ_DEBUG(10, slq, "destroying");
586
587         /* Free all entries before freeing the slq handle! */
588         TALLOC_FREE(slq->entries_ctx);
589         TALLOC_FREE(slq->te);
590
591         if (slq->mds_ctx != NULL) {
592                 DLIST_REMOVE(slq->mds_ctx->query_list, slq);
593                 slq->mds_ctx = NULL;
594         }
595
596         if (slq->tracker_cursor != NULL) {
597                 g_object_unref(slq->tracker_cursor);
598                 slq->tracker_cursor = NULL;
599         }
600
601         if (slq->gcancellable != NULL) {
602                 g_cancellable_cancel(slq->gcancellable);
603                 g_object_unref(slq->gcancellable);
604                 slq->gcancellable = NULL;
605         }
606
607         return 0;
608 }
609
610 /**
611  * Remove talloc_refcounted entry from mapping db
612  *
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.
617  **/
618 static int ino_path_map_destr_cb(struct sl_inode_path_map *entry)
619 {
620         NTSTATUS status;
621         TDB_DATA key;
622
623         key = make_tdb_data((uint8_t *)&entry->ino, sizeof(entry->ino));
624
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;
629         }
630
631         DEBUG(10,("deleted: %s\n", entry->path));
632         return 0;
633 }
634
635 /**
636  * Add result to inode->path mapping dbwrap rbt db
637  *
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.
641  *
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.
645  **/
646
647 static bool inode_map_add(struct sl_query *slq, uint64_t ino, const char *path)
648 {
649         NTSTATUS status;
650         struct sl_inode_path_map *entry;
651         TDB_DATA key, value;
652         void *p;
653
654         key = make_tdb_data((uint8_t *)&ino, sizeof(ino));
655         status = dbwrap_fetch(slq->mds_ctx->ino_path_map, slq, key, &value);
656
657         if (NT_STATUS_IS_OK(status)) {
658                 /*
659                  * We have one db, so when different parallel queries
660                  * return the same file, we have to refcount entries
661                  * in the db.
662                  */
663
664                 if (value.dsize != sizeof(void *)) {
665                         DEBUG(1, ("invalide dsize\n"));
666                         return false;
667                 }
668                 memcpy(&p, value.dptr, sizeof(p));
669                 entry = talloc_get_type_abort(p, struct sl_inode_path_map);
670
671                 DEBUG(10, ("map: %s\n", entry->path));
672
673                 entry = talloc_reference(slq->entries_ctx, entry);
674                 if (entry == NULL) {
675                         DEBUG(1, ("talloc_reference failed\n"));
676                         return false;
677                 }
678                 return true;
679         }
680
681         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
682                 DEBUG(1, ("dbwrap_fetch failed %s\n", nt_errstr(status)));
683                 return false;
684         }
685
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;
690         }
691
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;
699         }
700
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;
707         }
708
709         talloc_set_destructor(entry, ino_path_map_destr_cb);
710
711         return true;
712 }
713
714 /************************************************
715  * Tracker async callbacks
716  ************************************************/
717
718 static void tracker_con_cb(GObject *object,
719                            GAsyncResult *res,
720                            gpointer user_data)
721 {
722         struct mds_ctx *mds_ctx = talloc_get_type_abort(user_data, struct mds_ctx);
723         GError *error = NULL;
724
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);
731         }
732
733         DEBUG(10, ("connected to Tracker\n"));
734         g_main_loop_quit(mds_ctx->gmainloop);
735 }
736
737 static void tracker_cursor_cb_destroy_done(struct tevent_req *subreq);
738
739 static void tracker_cursor_cb(GObject *object,
740                               GAsyncResult *res,
741                               gpointer user_data)
742 {
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;
753
754         SLQ_DEBUG(10, slq, "tracker_cursor_cb");
755
756         more_results = tracker_sparql_cursor_next_finish(slq->tracker_cursor,
757                                                          res,
758                                                          &error);
759
760         if (slq->state == SLQ_STATE_DONE) {
761                 /*
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.
766                  *
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.
771                  */
772                 SLQ_DEBUG(10, slq, "closed");
773                 g_main_loop_quit(slq->mds_ctx->gmainloop);
774
775                 req = slq_destroy_send(slq, server_event_context(), &slq);
776                 if (req == NULL) {
777                         slq->state = SLQ_STATE_ERROR;
778                         return;
779                 }
780                 tevent_req_set_callback(req, tracker_cursor_cb_destroy_done, NULL);
781                 return;
782         }
783
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;
790         }
791
792         if (!more_results) {
793                 slq->state = SLQ_STATE_DONE;
794                 g_main_loop_quit(slq->mds_ctx->gmainloop);
795                 return;
796         }
797
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;
804         }
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;
811         }
812
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");
816         }
817
818         result = sys_stat(path, &sb, false);
819         if (result != 0) {
820                 goto done;
821         }
822         result = access(path, R_OK);
823         if (result != 0) {
824                 goto done;
825         }
826
827         ino64 = sb.st_ex_ino;
828         if (slq->cnids) {
829                 /*
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
833                  */
834                 ok = bsearch(&ino64, slq->cnids, slq->cnids_num,
835                              sizeof(uint64_t), cnid_comp_fn);
836                 if (!ok) {
837                         goto done;
838                 }
839         }
840
841         /*
842          * Add inode number and filemeta to result set, this is what
843          * we return as part of the result set of a query
844          */
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;
852         }
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;
860         }
861
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;
868         }
869
870         slq->query_results->num_results++;
871
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;
878         }
879
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);
886 }
887
888 static void tracker_cursor_cb_destroy_done(struct tevent_req *req)
889 {
890         slq_destroy_recv(req);
891         TALLOC_FREE(req);
892
893         DEBUG(10, ("%s\n", __func__));
894 }
895
896 static void tracker_query_cb(GObject *object,
897                              GAsyncResult *res,
898                              gpointer user_data)
899 {
900         GError *error = NULL;
901         struct sl_query *slq = talloc_get_type_abort(user_data, struct sl_query);
902
903         SLQ_DEBUG(10, slq, "tracker_query_cb");
904
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;
915         }
916
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;
922         }
923
924         slq->state = SLQ_STATE_RESULTS;
925
926         tracker_sparql_cursor_next_async(slq->tracker_cursor,
927                                          slq->gcancellable,
928                                          tracker_cursor_cb,
929                                          slq);
930 }
931
932 /***********************************************************
933  * Spotlight RPC functions
934  ***********************************************************/
935
936 static bool slrpc_fetch_properties(struct mds_ctx *mds_ctx,
937                                    const DALLOC_CTX *query, DALLOC_CTX *reply)
938 {
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;
946
947         dict = dalloc_zero(reply, sl_dict_t);
948         if (dict == NULL) {
949                 return false;
950         }
951
952         /* kMDSStoreHasPersistentUUID = false */
953         result = dalloc_stradd(dict, "kMDSStoreHasPersistentUUID");
954         if (result != 0) {
955                 return false;
956         }
957         b = false;
958         result = dalloc_add_copy(dict, &b, sl_bool_t);
959         if (result != 0) {
960                 return false;
961         }
962
963         /* kMDSStoreIsBackup = false */
964         result = dalloc_stradd(dict, "kMDSStoreIsBackup");
965         if (result != 0) {
966                 return false;
967         }
968         b = false;
969         result = dalloc_add_copy(dict, &b, sl_bool_t);
970         if (result != 0) {
971                 return false;
972         }
973
974         /* kMDSStoreUUID = uuid */
975         result = dalloc_stradd(dict, "kMDSStoreUUID");
976         if (result != 0) {
977                 return false;
978         }
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;
983         }
984
985         /* kMDSStoreSupportsVolFS = true */
986         result = dalloc_stradd(dict, "kMDSStoreSupportsVolFS");
987         if (result != 0) {
988                 return false;
989         }
990         b = true;
991         result = dalloc_add_copy(dict, &b, sl_bool_t);
992         if (result != 0) {
993                 return false;
994         }
995
996         /* kMDSVolumeUUID = uuid */
997         result = dalloc_stradd(dict, "kMDSVolumeUUID");
998         if (result != 0) {
999                 return false;
1000         }
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;
1005         }
1006
1007         /* kMDSDiskStoreSpindleNumber = 1 (fake) */
1008         result = dalloc_stradd(dict, "kMDSDiskStoreSpindleNumber");
1009         if (result != 0) {
1010                 return false;
1011         }
1012         u = 1;
1013         result = dalloc_add_copy(dict, &u, uint64_t);
1014         if (result != 0) {
1015                 return false;
1016         }
1017
1018         /* kMDSDiskStorePolicy = 3 (whatever that means, taken from OS X) */
1019         result = dalloc_stradd(dict, "kMDSDiskStorePolicy");
1020         if (result != 0) {
1021                 return false;
1022         }
1023         u = 3;
1024         result = dalloc_add_copy(dict, &u, uint64_t);
1025         if (result != 0) {
1026                 return false;
1027         }
1028
1029         /* kMDSStoreMetaScopes array */
1030         array = dalloc_zero(dict, sl_array_t);
1031         if (array == NULL) {
1032                 return NULL;
1033         }
1034         result = dalloc_stradd(array, "kMDQueryScopeComputer");
1035         if (result != 0) {
1036                 return false;
1037         }
1038         result = dalloc_stradd(array, "kMDQueryScopeAllIndexed");
1039         if (result != 0) {
1040                 return false;
1041         }
1042         result = dalloc_stradd(array, "kMDQueryScopeComputerIndexed");
1043         if (result != 0) {
1044                 return false;
1045         }
1046         result = dalloc_add(dict, array, sl_array_t);
1047         if (result != 0) {
1048                 return false;
1049         }
1050
1051         /* kMDSStoreDevice = 0x1000003 (whatever that means, taken from OS X) */
1052         result = dalloc_stradd(dict, "kMDSStoreDevice");
1053         if (result != 0) {
1054                 return false;
1055         }
1056         u = 0x1000003;
1057         result = dalloc_add_copy(dict, &u, uint64_t);
1058         if (result != 0) {
1059                 return false;
1060         }
1061
1062         /* kMDSStoreSupportsTCC = true (whatever that means, taken from OS X) */
1063         result = dalloc_stradd(dict, "kMDSStoreSupportsTCC");
1064         if (result != 0) {
1065                 return false;
1066         }
1067         b = true;
1068         result = dalloc_add_copy(dict, &b, sl_bool_t);
1069         if (result != 0) {
1070                 return false;
1071         }
1072
1073         /* kMDSStorePathScopes = ["/"] (whatever that means, taken from OS X) */
1074         result = dalloc_stradd(dict, "kMDSStorePathScopes");
1075         if (result != 0) {
1076                 return false;
1077         }
1078         array = dalloc_zero(dict, sl_array_t);
1079         if (array == NULL) {
1080                 return false;
1081         }
1082         s = talloc_strdup(dict, "/");
1083         if (s == NULL) {
1084                 return false;
1085         }
1086         talloc_set_name(s, "smb_ucs2_t *");
1087         result = dalloc_add(array, s, smb_ucs2_t *);
1088         if (result != 0) {
1089                 return false;
1090         }
1091         result = dalloc_add(dict, array, sl_array_t);
1092         if (result != 0) {
1093                 return false;
1094         }
1095
1096         result = dalloc_add(reply, dict, sl_dict_t);
1097         if (result != 0) {
1098                 return false;
1099         }
1100
1101         return true;
1102 }
1103
1104 static void slq_close_timer(struct tevent_context *ev,
1105                             struct tevent_timer *te,
1106                             struct timeval current_time,
1107                             void *private_data)
1108 {
1109         struct sl_query *slq = talloc_get_type_abort(
1110                 private_data, struct sl_query);
1111         struct mds_ctx *mds_ctx = slq->mds_ctx;
1112
1113         SLQ_DEBUG(10, slq, "expired");
1114
1115         TALLOC_FREE(slq);
1116
1117         if (CHECK_DEBUGLVL(10)) {
1118                 for (slq = mds_ctx->query_list; slq != NULL; slq = slq->next) {
1119                         SLQ_DEBUG(10, slq, "pending");
1120                 }
1121         }
1122 }
1123
1124 /**
1125  * Begin a search query
1126  **/
1127 static bool slrpc_open_query(struct mds_ctx *mds_ctx,
1128                              const DALLOC_CTX *query, DALLOC_CTX *reply)
1129 {
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;
1139
1140         array = dalloc_zero(reply, sl_array_t);
1141         if (array == NULL) {
1142                 return false;
1143         }
1144
1145         if (mds_ctx->tracker_con == NULL) {
1146                 DEBUG(1, ("no connection to Tracker\n"));
1147                 goto error;
1148         }
1149
1150         /* Allocate and initialize query object */
1151         slq = talloc_zero(mds_ctx, struct sl_query);
1152         if (slq == NULL) {
1153                 return false;
1154         }
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;
1159         }
1160         talloc_set_destructor(slq, slq_destructor_cb);
1161         slq->state = SLQ_STATE_NEW;
1162         slq->mds_ctx = mds_ctx;
1163
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;
1172         }
1173
1174         slq->gcancellable = g_cancellable_new();
1175         if (slq->gcancellable == NULL) {
1176                 DEBUG(1,("error from g_cancellable_new\n"));
1177                 goto error;
1178         }
1179
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;
1186         }
1187         slq->query_string = talloc_strdup(slq, querystring);
1188         if (slq->query_string == NULL) {
1189                 DEBUG(1, ("out of memory\n"));
1190                 goto error;
1191         }
1192
1193         /*
1194          * FIXME: convert spotlight query charset from decomposed UTF8
1195          * to host charset precomposed UTF8.
1196          */
1197
1198         uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1199                              "uint64_t", 1);
1200         if (uint64p == NULL) {
1201                 goto error;
1202         }
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;
1208         }
1209         slq->ctx2 = *uint64p;
1210
1211         path_scope = dalloc_value_for_key(query, "DALLOC_CTX", 0,
1212                                           "DALLOC_CTX", 1, "kMDScopeArray");
1213         if (path_scope == NULL) {
1214                 goto error;
1215         }
1216
1217         slq->path_scope = dalloc_get(path_scope, "char *", 0);
1218         if (slq->path_scope == NULL) {
1219                 goto error;
1220         }
1221
1222         slq->path_scope = talloc_strdup(slq, slq->path_scope);
1223         if (slq->path_scope == NULL) {
1224                 goto error;
1225         }
1226
1227
1228         reqinfo = dalloc_value_for_key(query, "DALLOC_CTX", 0,
1229                                        "DALLOC_CTX", 1, "kMDAttributeArray");
1230         if (reqinfo == NULL) {
1231                 goto error;
1232         }
1233
1234         slq->reqinfo = talloc_steal(slq, reqinfo);
1235         DEBUG(10, ("requested attributes: %s", mds_dalloc_dump(reqinfo, 0)));
1236
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;
1243                 }
1244         }
1245
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;
1251         }
1252
1253         SLQ_DEBUG(10, slq, "new");
1254
1255         DLIST_ADD(mds_ctx->query_list, slq);
1256
1257         ok = map_spotlight_to_sparql_query(slq);
1258         if (!ok) {
1259                 /*
1260                  * Two cases:
1261                  *
1262                  * 1) the query string is "false", the parser returns
1263                  * an error for that. We're supposed to return -1
1264                  * here.
1265                  *
1266                  * 2) the parsing really failed, in that case we're
1267                  * probably supposed to return -1 too, this needs
1268                  * verification though
1269                  */
1270                 SLQ_DEBUG(10, slq, "map failed");
1271                 goto error;
1272         }
1273
1274         DEBUG(10, ("SPARQL query: \"%s\"\n", slq->sparql_query));
1275
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;
1284
1285         sl_result = 0;
1286         result = dalloc_add_copy(array, &sl_result, uint64_t);
1287         if (result != 0) {
1288                 goto error;
1289         }
1290         result = dalloc_add(reply, array, sl_array_t);
1291         if (result != 0) {
1292                 goto error;
1293         }
1294         return true;
1295
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;
1302         }
1303         result = dalloc_add(reply, array, sl_array_t);
1304         if (result != 0) {
1305                 return false;
1306         }
1307         return true;
1308 }
1309
1310 /**
1311  * Fetch results of a query
1312  **/
1313 static bool slrpc_fetch_query_results(struct mds_ctx *mds_ctx,
1314                                       const DALLOC_CTX *query,
1315                                       DALLOC_CTX *reply)
1316 {
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;
1323
1324         array = dalloc_zero(reply, sl_array_t);
1325         if (array == NULL) {
1326                 return false;
1327         }
1328
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;
1334         }
1335         ctx1 = *uint64p;
1336
1337         uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1338                              "uint64_t", 2);
1339         if (uint64p == NULL) {
1340                 goto error;
1341         }
1342         ctx2 = *uint64p;
1343
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;
1349         }
1350
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;
1359         }
1360
1361         SLQ_DEBUG(10, slq, "fetch");
1362
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;
1372                 }
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);
1382                 }
1383                 break;
1384
1385         case SLQ_STATE_ERROR:
1386                 DEBUG(1, ("query in error state\n"));
1387                 goto error;
1388
1389         default:
1390                 DEBUG(1, ("unexpected query state %d\n", slq->state));
1391                 goto error;
1392         }
1393
1394         result = dalloc_add(reply, array, sl_array_t);
1395         if (result != 0) {
1396                 goto error;
1397         }
1398         return true;
1399
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;
1406         }
1407         result = dalloc_add(reply, array, sl_array_t);
1408         if (result != 0) {
1409                 return false;
1410         }
1411         return true;
1412 }
1413
1414 /**
1415  * Store metadata attributes for a CNID
1416  **/
1417 static bool slrpc_store_attributes(struct mds_ctx *mds_ctx,
1418                                    const DALLOC_CTX *query, DALLOC_CTX *reply)
1419 {
1420         uint64_t sl_result;
1421         sl_array_t *array;
1422         int result;
1423
1424         array = dalloc_zero(reply, sl_array_t);
1425         if (array == NULL) {
1426                 return false;
1427         }
1428
1429         /*
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.
1434          */
1435
1436         sl_result = 0;
1437         result = dalloc_add_copy(array, &sl_result, uint64_t);
1438         if (result != 0) {
1439                 return false;
1440         }
1441         result = dalloc_add(reply, array, sl_array_t);
1442         if (result != 0) {
1443                 return false;
1444         }
1445
1446         return true;
1447 }
1448
1449 /**
1450  * Fetch supported metadata attributes for a CNID
1451  **/
1452 static bool slrpc_fetch_attributenames(struct mds_ctx *mds_ctx,
1453                                        const DALLOC_CTX *query,
1454                                        DALLOC_CTX *reply)
1455 {
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;
1465
1466         cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 1);
1467         if (cnids == NULL) {
1468                 return false;
1469         }
1470
1471         p = dalloc_get_object(cnids->ca_cnids, 0);
1472         if (p == NULL) {
1473                 return NULL;
1474         }
1475         memcpy(&id, p, sizeof(uint64_t));
1476
1477         /* Result array */
1478         array = dalloc_zero(reply, sl_array_t);
1479         if (array == NULL) {
1480                 return false;
1481         }
1482
1483         result = dalloc_add(reply, array, sl_array_t);
1484         if (result != 0) {
1485                 return false;
1486         }
1487
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;
1493         }
1494
1495         /* Return CNID array */
1496         replycnids = talloc_zero(reply, sl_cnids_t);
1497         if (replycnids == NULL) {
1498                 return false;
1499         }
1500
1501         replycnids->ca_cnids = dalloc_new(cnids);
1502         if (replycnids->ca_cnids == NULL) {
1503                 return false;
1504         }
1505
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;
1511         }
1512         result = dalloc_add(array, replycnids, sl_cnids_t);
1513         if (result != 0) {
1514                 return false;
1515         }
1516
1517         /*
1518          * FIXME: this should return the real attributes from all
1519          * known metadata sources (Tracker and filesystem)
1520          */
1521         mdattrs = dalloc_zero(reply, sl_array_t);
1522         if (mdattrs == NULL) {
1523                 return false;
1524         }
1525
1526         result = dalloc_stradd(mdattrs, "kMDItemFSName");
1527         if (result != 0) {
1528                 return false;
1529         }
1530         result = dalloc_stradd(mdattrs, "kMDItemDisplayName");
1531         if (result != 0) {
1532                 return false;
1533         }
1534         result = dalloc_stradd(mdattrs, "kMDItemFSSize");
1535         if (result != 0) {
1536                 return false;
1537         }
1538         result = dalloc_stradd(mdattrs, "kMDItemFSOwnerUserID");
1539         if (result != 0) {
1540                 return false;
1541         }
1542         result = dalloc_stradd(mdattrs, "kMDItemFSOwnerGroupID");
1543         if (result != 0) {
1544                 return false;
1545         }
1546         result = dalloc_stradd(mdattrs, "kMDItemFSContentChangeDate");
1547         if (result != 0) {
1548                 return false;
1549         }
1550
1551         fmeta = dalloc_zero(reply, sl_filemeta_t);
1552         if (fmeta == NULL) {
1553                 return false;
1554         }
1555         result = dalloc_add(fmeta, mdattrs, sl_array_t);
1556         if (result != 0) {
1557                 return false;
1558         }
1559         result = dalloc_add(array, fmeta, sl_filemeta_t);
1560         if (result != 0) {
1561                 return false;
1562         }
1563
1564         return true;
1565 }
1566
1567 /**
1568  * Fetch metadata attribute values for a CNID
1569  **/
1570 static bool slrpc_fetch_attributes(struct mds_ctx *mds_ctx,
1571                                    const DALLOC_CTX *query, DALLOC_CTX *reply)
1572 {
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;
1589
1590         array = dalloc_zero(reply, sl_array_t);
1591         if (array == NULL) {
1592                 return false;
1593         }
1594         replycnids = talloc_zero(reply, sl_cnids_t);
1595         if (replycnids == NULL) {
1596                 goto error;
1597         }
1598         replycnids->ca_cnids = dalloc_new(replycnids);
1599         if (replycnids->ca_cnids == NULL) {
1600                 goto error;
1601         }
1602         fm = dalloc_zero(array, sl_filemeta_t);
1603         if (fm == NULL) {
1604                 goto error;
1605         }
1606         fm_array = dalloc_zero(fm, sl_array_t);
1607         if (fm_array == NULL) {
1608                 goto error;
1609         }
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;
1614         }
1615
1616         reqinfo = dalloc_get(query, "DALLOC_CTX", 0, "sl_array_t", 1);
1617         if (reqinfo == NULL) {
1618                 goto error;
1619         }
1620
1621         cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 2);
1622         if (cnids == NULL) {
1623                 goto error;
1624         }
1625         p = dalloc_get_object(cnids->ca_cnids, 0);
1626         if (p == NULL) {
1627                 goto error;
1628         }
1629         memcpy(&ino, p, sizeof(uint64_t));
1630
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;
1636         }
1637
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;
1644         }
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;
1649         }
1650
1651         memcpy(&p, val.dptr, sizeof(p));
1652         elem = talloc_get_type_abort(p, struct sl_inode_path_map);
1653
1654         result = sys_stat(elem->path, &sb, false);
1655         if (result != 0) {
1656                 goto error;
1657         }
1658
1659         ok = add_filemeta(reqinfo, fm_array, elem->path, &sb);
1660         if (!ok) {
1661                 goto error;
1662         }
1663
1664         sl_result = 0;
1665         result = dalloc_add_copy(array, &sl_result, uint64_t);
1666         if (result != 0) {
1667                 goto error;
1668         }
1669         result = dalloc_add(array, replycnids, sl_cnids_t);
1670         if (result != 0) {
1671                 goto error;
1672         }
1673         result = dalloc_add(fm, fm_array, sl_array_t);
1674         if (result != 0) {
1675                 goto error;
1676         }
1677         result = dalloc_add(array, fm, sl_filemeta_t);
1678         if (result != 0) {
1679                 goto error;
1680         }
1681         result = dalloc_add(reply, array, sl_array_t);
1682         if (result != 0) {
1683                 goto error;
1684         }
1685
1686         return true;
1687
1688 error:
1689         sl_result = UINT64_MAX;
1690         result = dalloc_add_copy(array, &sl_result, uint64_t);
1691         if (result != 0) {
1692                 return false;
1693         }
1694         result = dalloc_add(reply, array, sl_array_t);
1695         if (result != 0) {
1696                 return false;
1697         }
1698
1699         return true;
1700 }
1701
1702 /**
1703  * Close a query
1704  **/
1705 static bool slrpc_close_query(struct mds_ctx *mds_ctx,
1706                               const DALLOC_CTX *query, DALLOC_CTX *reply)
1707 {
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;
1713
1714         array = dalloc_zero(reply, sl_array_t);
1715         if (array == NULL) {
1716                 return false;
1717         }
1718
1719         /* Context */
1720         uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1721                              "uint64_t", 1);
1722         if (uint64p == NULL) {
1723                 goto done;
1724         }
1725         ctx1 = *uint64p;
1726
1727         uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1728                              "uint64_t", 2);
1729         if (uint64p == NULL) {
1730                 goto done;
1731         }
1732         ctx2 = *uint64p;
1733
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;
1740         }
1741
1742         switch (slq->state) {
1743         case SLQ_STATE_RUNNING:
1744         case SLQ_STATE_RESULTS:
1745                 DEBUG(10, ("close: requesting query close\n"));
1746                 /*
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.
1751                  */
1752                 slq->state = SLQ_STATE_DONE;
1753                 break;
1754
1755         case SLQ_STATE_FULL:
1756         case SLQ_STATE_DONE:
1757                 DEBUG(10, ("close: query was done or result queue was full\n"));
1758                 /*
1759                  * We can directly deallocate the query because there
1760                  * are no pending Tracker async calls in flight in
1761                  * these query states.
1762                  */
1763                 TALLOC_FREE(slq);
1764                 break;
1765
1766         default:
1767                 DEBUG(1, ("close: unexpected state: %d\n", slq->state));
1768                 break;
1769         }
1770
1771
1772 done:
1773         sl_res = 0;
1774         result = dalloc_add_copy(array, &sl_res, uint64_t);
1775         if (result != 0) {
1776                 return false;
1777         }
1778         result = dalloc_add(reply, array, sl_array_t);
1779         if (result != 0) {
1780                 return false;
1781         }
1782         return true;
1783 }
1784
1785 /**
1786  * Init callbacks at startup
1787  **/
1788 bool mds_init(struct messaging_context *msg_ctx)
1789 {
1790 #if (GLIB_MAJOR_VERSION < 3) && (GLIB_MINOR_VERSION < 36)
1791         g_type_init();
1792 #endif
1793         return true;
1794 }
1795
1796 bool mds_shutdown(void)
1797 {
1798         return true;
1799 }
1800
1801 static gboolean gmainloop_timer(gpointer user_data)
1802 {
1803         struct mds_ctx *ctx = talloc_get_type_abort(user_data, struct mds_ctx);
1804
1805         DEBUG(10,("%s\n", __func__));
1806         g_main_loop_quit(ctx->gmainloop);
1807
1808         return G_SOURCE_CONTINUE;
1809 }
1810
1811 /**
1812  * Initialise a context per share handle
1813  **/
1814 struct mds_ctx *mds_init_ctx(TALLOC_CTX *mem_ctx,
1815                              const struct auth_session_info *session_info,
1816                              const char *path)
1817 {
1818         struct mds_ctx *mds_ctx;
1819
1820         mds_ctx = talloc_zero(mem_ctx, struct mds_ctx);
1821         if (mds_ctx == NULL) {
1822                 return NULL;
1823         }
1824         talloc_set_destructor(mds_ctx, mds_ctx_destructor_cb);
1825
1826         mds_ctx->spath = talloc_strdup(mds_ctx, path);
1827         if (mds_ctx->spath == NULL) {
1828                 goto error;
1829         }
1830
1831         if (session_info->security_token->num_sids < 1) {
1832                 goto error;
1833         }
1834         sid_copy(&mds_ctx->sid, &session_info->security_token->sids[0]);
1835         mds_ctx->uid = session_info->unix_token->uid;
1836
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"));
1840                 goto error;
1841         }
1842
1843         mds_ctx->gcontext = g_main_context_new();
1844         if (mds_ctx->gcontext == NULL) {
1845                 DEBUG(1,("error from g_main_context_new\n"));
1846                 goto error;
1847         }
1848
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"));
1852                 goto error;
1853         }
1854
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);
1859
1860         return mds_ctx;
1861
1862 error:
1863         TALLOC_FREE(mds_ctx);
1864         return NULL;
1865 }
1866
1867 /**
1868  * Tear down connections and free all resources
1869  **/
1870 int mds_ctx_destructor_cb(struct mds_ctx *mds_ctx)
1871 {
1872         /*
1873          * We need to free query_list before ino_path_map
1874          */
1875         while (mds_ctx->query_list != NULL) {
1876                 /*
1877                  * slq destructor removes element from list.
1878                  * Don't use TALLOC_FREE()!
1879                  */
1880                 talloc_free(mds_ctx->query_list);
1881         }
1882         TALLOC_FREE(mds_ctx->ino_path_map);
1883
1884         if (mds_ctx->tracker_con != NULL) {
1885                 g_object_unref(mds_ctx->tracker_con);
1886         }
1887         if (mds_ctx->gcancellable != NULL) {
1888                 g_cancellable_cancel(mds_ctx->gcancellable);
1889                 g_object_unref(mds_ctx->gcancellable);
1890         }
1891         if (mds_ctx->gmainloop != NULL) {
1892                 g_main_loop_unref(mds_ctx->gmainloop);
1893         }
1894         if (mds_ctx->gcontext != NULL) {
1895                 g_main_context_unref(mds_ctx->gcontext);
1896         }
1897
1898         ZERO_STRUCTP(mds_ctx);
1899
1900         return 0;
1901 }
1902
1903 static bool mds_run_gmainloop(struct mds_ctx *mds_ctx, guint timeout)
1904 {
1905         guint timer_id;
1906         GSource *timer;
1907
1908         /*
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.
1912          *
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.
1918          *
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.
1923          *
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.
1928          */
1929
1930         if (!g_main_context_pending(mds_ctx->gcontext)) {
1931                 return true;
1932         }
1933
1934         g_main_context_push_thread_default(mds_ctx->gcontext);
1935
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);
1940                 return false;
1941         }
1942
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);
1948                 return false;
1949         }
1950
1951         g_source_set_callback(timer, gmainloop_timer, mds_ctx, NULL);
1952
1953         g_main_loop_run(mds_ctx->gmainloop);
1954
1955         g_source_destroy(timer);
1956
1957         g_main_context_pop_thread_default(mds_ctx->gcontext);
1958         return true;
1959 }
1960
1961 /**
1962  * Dispatch a Spotlight RPC command
1963  **/
1964 bool mds_dispatch(struct mds_ctx *mds_ctx,
1965                   struct mdssvc_blob *request_blob,
1966                   struct mdssvc_blob *response_blob)
1967 {
1968         bool ok;
1969         ssize_t len;
1970         DALLOC_CTX *query = NULL;
1971         DALLOC_CTX *reply = NULL;
1972         char *rpccmd;
1973         const struct slrpc_cmd *slcmd;
1974
1975         if (CHECK_DEBUGLVL(10)) {
1976                 const struct sl_query *slq;
1977
1978                 for (slq = mds_ctx->query_list; slq != NULL; slq = slq->next) {
1979                         SLQ_DEBUG(10, slq, "pending");
1980                 }
1981         }
1982
1983         response_blob->length = 0;
1984
1985         /*
1986          * Process finished glib events.
1987          *
1988          * FIXME: integrate with tevent instead of piggy packing it
1989          * onto the processing of new requests.
1990          *
1991          * mds_dispatch() is called by the client a few times in a row:
1992          *
1993          * - first in order to open/start a search query
1994          *
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.
2000          *
2001          * - at some point the query is closed
2002          *
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.
2006          */
2007
2008         ok = mds_run_gmainloop(mds_ctx, MDS_TRACKER_ASYNC_TIMEOUT_MS);
2009         if (!ok) {
2010                 goto cleanup;
2011         }
2012
2013         DEBUG(10, ("share path: %s\n", mds_ctx->spath));
2014
2015         query = dalloc_new(mds_ctx);
2016         if (query == NULL) {
2017                 ok = false;
2018                 goto cleanup;
2019         }
2020         reply = dalloc_new(mds_ctx);
2021         if (reply == NULL) {
2022                 ok = false;
2023                 goto cleanup;
2024         }
2025
2026         ok = sl_unpack(query, (char *)request_blob->spotlight_blob,
2027                        request_blob->length);
2028         if (!ok) {
2029                 DEBUG(1, ("error unpacking Spotlight RPC blob\n"));
2030                 goto cleanup;
2031         }
2032
2033         DEBUG(5, ("%s", mds_dalloc_dump(query, 0)));
2034
2035         rpccmd = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
2036                             "char *", 0);
2037         if (rpccmd == NULL) {
2038                 DEBUG(1, ("missing primary Spotlight RPC command\n"));
2039                 ok = false;
2040                 goto cleanup;
2041         }
2042
2043         DEBUG(10, ("Spotlight RPC cmd: %s\n", rpccmd));
2044
2045         slcmd = slrpc_cmd_by_name(rpccmd);
2046         if (slcmd == NULL) {
2047                 DEBUG(1, ("unsupported primary Spotlight RPC command %s\n",
2048                           rpccmd));
2049                 ok = false;
2050                 goto cleanup;
2051         }
2052
2053         /*
2054          * If these functions return an error, they hit something like
2055          * a non recoverable talloc error
2056          */
2057         ok = slcmd->function(mds_ctx, query, reply);
2058         if (!ok) {
2059                 DEBUG(1, ("error in Spotlight RPC handler\n"));
2060                 goto cleanup;
2061         }
2062
2063         DEBUG(5, ("%s", mds_dalloc_dump(reply, 0)));
2064
2065         len = sl_pack(reply, (char *)response_blob->spotlight_blob,
2066                       response_blob->size);
2067         if (len == -1) {
2068                 DEBUG(1, ("error packing Spotlight RPC reply\n"));
2069                 ok = false;
2070                 goto cleanup;
2071         }
2072
2073         /*
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.
2078          */
2079         ok = mds_run_gmainloop(mds_ctx, MDS_TRACKER_ASYNC_TIMEOUT_MS / 10);
2080         if (!ok) {
2081                 goto cleanup;
2082         }
2083
2084         response_blob->length = len;
2085
2086 cleanup:
2087         talloc_free(query);
2088         talloc_free(reply);
2089         return ok;
2090 }