mdssvc: better support for search with mdfind from Macs
[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 "smbd/proto.h"
23 #include "librpc/gen_ndr/auth.h"
24 #include "dbwrap/dbwrap.h"
25 #include "lib/util/dlinklist.h"
26 #include "lib/util/util_tdb.h"
27 #include "lib/util/time_basic.h"
28 #include "lib/dbwrap/dbwrap_rbt.h"
29 #include "libcli/security/dom_sid.h"
30 #include "libcli/security/security.h"
31 #include "mdssvc.h"
32 #include "mdssvc_noindex.h"
33 #ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
34 #include "mdssvc_tracker.h"
35 #endif
36 #ifdef HAVE_SPOTLIGHT_BACKEND_ES
37 #include "mdssvc_es.h"
38 #endif
39 #include "lib/global_contexts.h"
40
41 #undef DBGC_CLASS
42 #define DBGC_CLASS DBGC_RPC_SRV
43
44 struct slrpc_cmd {
45         const char *name;
46         bool (*function)(struct mds_ctx *mds_ctx,
47                          const DALLOC_CTX *query,
48                          DALLOC_CTX *reply);
49 };
50
51 struct slq_destroy_state {
52         struct tevent_context *ev;
53         struct sl_query *slq;
54 };
55
56 /*
57  * This is a static global because we may be called multiple times and
58  * we only want one mdssvc_ctx per connection to Tracker.
59  *
60  * The client will bind multiple times to the mdssvc RPC service, once
61  * for every tree connect.
62  */
63 static struct mdssvc_ctx *mdssvc_ctx = NULL;
64
65 /*
66  * If these functions return an error, they hit something like a non
67  * recoverable talloc error. Most errors are dealt with by returning
68  * an error code in the Spotlight RPC reply.
69  */
70 static bool slrpc_fetch_properties(struct mds_ctx *mds_ctx,
71                                    const DALLOC_CTX *query, DALLOC_CTX *reply);
72 static bool slrpc_open_query(struct mds_ctx *mds_ctx,
73                              const DALLOC_CTX *query, DALLOC_CTX *reply);
74 static bool slrpc_fetch_query_results(struct mds_ctx *mds_ctx,
75                                       const DALLOC_CTX *query, DALLOC_CTX *reply);
76 static bool slrpc_store_attributes(struct mds_ctx *mds_ctx,
77                                    const DALLOC_CTX *query, DALLOC_CTX *reply);
78 static bool slrpc_fetch_attributenames(struct mds_ctx *mds_ctx,
79                                        const DALLOC_CTX *query, DALLOC_CTX *reply);
80 static bool slrpc_fetch_attributes(struct mds_ctx *mds_ctx,
81                                    const DALLOC_CTX *query, DALLOC_CTX *reply);
82 static bool slrpc_close_query(struct mds_ctx *mds_ctx,
83                               const DALLOC_CTX *query, DALLOC_CTX *reply);
84
85 /************************************************
86  * Misc utility functions
87  ************************************************/
88
89 /**
90  * Add requested metadata for a query result element
91  *
92  * This could be rewritten to something more sophisticated like
93  * querying metadata from Tracker.
94  *
95  * If path or sp is NULL, simply add nil values for all attributes.
96  **/
97 static bool add_filemeta(struct mds_ctx *mds_ctx,
98                          sl_array_t *reqinfo,
99                          sl_array_t *fm_array,
100                          const char *path,
101                          const struct stat_ex *sp)
102 {
103         sl_array_t *meta;
104         sl_nil_t nil;
105         int i, metacount, result;
106         uint64_t uint64var;
107         sl_time_t sl_time;
108         char *p;
109         const char *attribute;
110         size_t nfc_len;
111         const char *nfc_path = path;
112         size_t nfd_buf_size;
113         char *nfd_path = NULL;
114         char *dest = NULL;
115         size_t dest_remaining;
116         size_t nconv;
117
118         metacount = dalloc_size(reqinfo);
119         if (metacount == 0 || path == NULL || sp == NULL) {
120                 result = dalloc_add_copy(fm_array, &nil, sl_nil_t);
121                 if (result != 0) {
122                         return false;
123                 }
124                 return true;
125         }
126
127         meta = dalloc_zero(fm_array, sl_array_t);
128         if (meta == NULL) {
129                 return false;
130         }
131
132         nfc_len = strlen(nfc_path);
133         /*
134          * Simple heuristic, strlen by two should give enough room for NFC to
135          * NFD conversion.
136          */
137         nfd_buf_size = nfc_len * 2;
138         nfd_path = talloc_array(meta, char, nfd_buf_size);
139         if (nfd_path == NULL) {
140                 return false;
141         }
142         dest = nfd_path;
143         dest_remaining = talloc_array_length(dest);
144
145         nconv = smb_iconv(mds_ctx->ic_nfc_to_nfd,
146                           &nfc_path,
147                           &nfc_len,
148                           &dest,
149                           &dest_remaining);
150         if (nconv == (size_t)-1) {
151                 return false;
152         }
153
154         for (i = 0; i < metacount; i++) {
155                 attribute = dalloc_get_object(reqinfo, i);
156                 if (attribute == NULL) {
157                         return false;
158                 }
159                 if (strcmp(attribute, "kMDItemDisplayName") == 0
160                     || strcmp(attribute, "kMDItemFSName") == 0) {
161                         p = strrchr(nfd_path, '/');
162                         if (p) {
163                                 result = dalloc_stradd(meta, p + 1);
164                                 if (result != 0) {
165                                         return false;
166                                 }
167                         }
168                 } else if (strcmp(attribute, "kMDItemPath") == 0) {
169                         result = dalloc_stradd(meta, nfd_path);
170                         if (result != 0) {
171                                 return false;
172                         }
173                 } else if (strcmp(attribute, "kMDItemFSSize") == 0) {
174                         uint64var = sp->st_ex_size;
175                         result = dalloc_add_copy(meta, &uint64var, uint64_t);
176                         if (result != 0) {
177                                 return false;
178                         }
179                 } else if (strcmp(attribute, "kMDItemFSOwnerUserID") == 0) {
180                         uint64var = sp->st_ex_uid;
181                         result = dalloc_add_copy(meta, &uint64var, uint64_t);
182                         if (result != 0) {
183                                 return false;
184                         }
185                 } else if (strcmp(attribute, "kMDItemFSOwnerGroupID") == 0) {
186                         uint64var = sp->st_ex_gid;
187                         result = dalloc_add_copy(meta, &uint64var, uint64_t);
188                         if (result != 0) {
189                                 return false;
190                         }
191                 } else if (strcmp(attribute, "kMDItemFSContentChangeDate") == 0 ||
192                         strcmp(attribute, "kMDItemContentModificationDate") == 0)
193                 {
194                         sl_time = convert_timespec_to_timeval(sp->st_ex_mtime);
195                         result = dalloc_add_copy(meta, &sl_time, sl_time_t);
196                         if (result != 0) {
197                                 return false;
198                         }
199                 } else {
200                         result = dalloc_add_copy(meta, &nil, sl_nil_t);
201                         if (result != 0) {
202                                 return false;
203                         }
204                 }
205         }
206
207         result = dalloc_add(fm_array, meta, sl_array_t);
208         if (result != 0) {
209                 return false;
210         }
211         return true;
212 }
213
214 static int cnid_comp_fn(const void *p1, const void *p2)
215 {
216         const uint64_t *cnid1 = p1, *cnid2 = p2;
217         if (*cnid1 == *cnid2) {
218                 return 0;
219         }
220         if (*cnid1 < *cnid2) {
221                 return -1;
222         }
223         return 1;
224 }
225
226 /**
227  * Create a sorted copy of a CNID array
228  **/
229 static bool sort_cnids(struct sl_query *slq, const DALLOC_CTX *d)
230 {
231         uint64_t *cnids = NULL;
232         int i;
233         const void *p;
234
235         cnids = talloc_array(slq, uint64_t, dalloc_size(d));
236         if (cnids == NULL) {
237                 return false;
238         }
239
240         for (i = 0; i < dalloc_size(d); i++) {
241                 p = dalloc_get_object(d, i);
242                 if (p == NULL) {
243                         return NULL;
244                 }
245                 memcpy(&cnids[i], p, sizeof(uint64_t));
246         }
247         qsort(cnids, dalloc_size(d), sizeof(uint64_t), cnid_comp_fn);
248
249         slq->cnids = cnids;
250         slq->cnids_num = dalloc_size(d);
251
252         return true;
253 }
254
255 /**
256  * Allocate result handle used in the async Tracker cursor result
257  * handler for storing results
258  **/
259 static bool create_result_handle(struct sl_query *slq)
260 {
261         sl_nil_t nil = 0;
262         struct sl_rslts *query_results;
263         int result;
264
265         if (slq->query_results) {
266                 DEBUG(1, ("unexpected existing result handle\n"));
267                 return false;
268         }
269
270         query_results = talloc_zero(slq, struct sl_rslts);
271         if (query_results == NULL) {
272                 return false;
273         }
274
275         /* CNIDs */
276         query_results->cnids = talloc_zero(query_results, sl_cnids_t);
277         if (query_results->cnids == NULL) {
278                 return false;
279         }
280         query_results->cnids->ca_cnids = dalloc_new(query_results->cnids);
281         if (query_results->cnids->ca_cnids == NULL) {
282                 return false;
283         }
284
285         query_results->cnids->ca_unkn1 = 0xadd;
286         if (slq->ctx2 > UINT32_MAX) {
287                 DEBUG(1,("64bit ctx2 id too large: 0x%jx", (uintmax_t)slq->ctx2));
288                 return false;
289         }
290         query_results->cnids->ca_context = (uint32_t)slq->ctx2;
291
292         /* FileMeta */
293         query_results->fm_array = dalloc_zero(query_results, sl_array_t);
294         if (query_results->fm_array == NULL) {
295                 return false;
296         }
297
298         /* For some reason the list of results always starts with a nil entry */
299         result = dalloc_add_copy(query_results->fm_array, &nil, sl_nil_t);
300         if (result != 0) {
301                 return false;
302         }
303
304         slq->query_results = query_results;
305         return true;
306 }
307
308 static bool add_results(sl_array_t *array, struct sl_query *slq)
309 {
310         sl_filemeta_t *fm;
311         uint64_t status;
312         int result;
313         bool ok;
314
315         /*
316          * Taken from network traces against a macOS SMB Spotlight server: if
317          * the search is not finished yet in the backend macOS returns 0x23,
318          * otherwise 0x0.
319          */
320         if (slq->state >= SLQ_STATE_DONE) {
321                 status = 0;
322         } else {
323                 status = 0x23;
324         }
325
326         /* FileMeta */
327         fm = dalloc_zero(array, sl_filemeta_t);
328         if (fm == NULL) {
329                 return false;
330         }
331
332         result = dalloc_add_copy(array, &status, uint64_t);
333         if (result != 0) {
334                 return false;
335         }
336         result = dalloc_add(array, slq->query_results->cnids, sl_cnids_t);
337         if (result != 0) {
338                 return false;
339         }
340         if (slq->query_results->num_results > 0) {
341                 result = dalloc_add(fm, slq->query_results->fm_array, sl_array_t);
342                 if (result != 0) {
343                         return false;
344                 }
345         }
346         result = dalloc_add(array, fm, sl_filemeta_t);
347         if (result != 0) {
348                 return false;
349         }
350
351         /* This ensure the results get clean up after been sent to the client */
352         talloc_move(array, &slq->query_results);
353
354         ok = create_result_handle(slq);
355         if (!ok) {
356                 DEBUG(1, ("couldn't add result handle\n"));
357                 slq->state = SLQ_STATE_ERROR;
358                 return false;
359         }
360
361         return true;
362 }
363
364 static const struct slrpc_cmd *slrpc_cmd_by_name(const char *rpccmd)
365 {
366         size_t i;
367         static const struct slrpc_cmd cmds[] = {
368                 { "fetchPropertiesForContext:", slrpc_fetch_properties},
369                 { "openQueryWithParams:forContext:", slrpc_open_query},
370                 { "fetchQueryResultsForContext:", slrpc_fetch_query_results},
371                 { "storeAttributes:forOIDArray:context:", slrpc_store_attributes},
372                 { "fetchAttributeNamesForOIDArray:context:", slrpc_fetch_attributenames},
373                 { "fetchAttributes:forOIDArray:context:", slrpc_fetch_attributes},
374                 { "fetchAllAttributes:forOIDArray:context:", slrpc_fetch_attributes},
375                 { "closeQueryForContext:", slrpc_close_query},
376         };
377
378         for (i = 0; i < ARRAY_SIZE(cmds); i++) {
379                 int cmp;
380
381                 cmp = strcmp(cmds[i].name, rpccmd);
382                 if (cmp == 0) {
383                         return &cmds[i];
384                 }
385         }
386
387         return NULL;
388 }
389
390 /**
391  * Search the list of active queries given their context ids
392  **/
393 static struct sl_query *slq_for_ctx(struct mds_ctx *mds_ctx,
394                                     uint64_t ctx1, uint64_t ctx2)
395 {
396         struct sl_query *q;
397
398         for (q = mds_ctx->query_list; q; q = q->next) {
399                 if ((q->ctx1 == ctx1) && (q->ctx2 == ctx2)) {
400                         return q;
401                 }
402         }
403
404         return NULL;
405 }
406
407 static int slq_destructor_cb(struct sl_query *slq)
408 {
409         SLQ_DEBUG(10, slq, "destroying");
410
411         /* Free all entries before freeing the slq handle! */
412         TALLOC_FREE(slq->entries_ctx);
413         TALLOC_FREE(slq->te);
414
415         if (slq->mds_ctx != NULL) {
416                 DLIST_REMOVE(slq->mds_ctx->query_list, slq);
417                 slq->mds_ctx = NULL;
418         }
419
420         TALLOC_FREE(slq->backend_private);
421
422         return 0;
423 }
424
425 /**
426  * Remove talloc_refcounted entry from mapping db
427  *
428  * Multiple queries (via the slq handle) may reference a
429  * sl_inode_path_map entry, when the last reference goes away as the
430  * queries are closed and this gets called to remove the entry from
431  * the db.
432  **/
433 static int ino_path_map_destr_cb(struct sl_inode_path_map *entry)
434 {
435         NTSTATUS status;
436         TDB_DATA key;
437
438         key = make_tdb_data((uint8_t *)&entry->ino, sizeof(entry->ino));
439
440         status = dbwrap_delete(entry->mds_ctx->ino_path_map, key);
441         if (!NT_STATUS_IS_OK(status)) {
442                 DEBUG(1, ("Failed to delete record: %s\n", nt_errstr(status)));
443                 return -1;
444         }
445
446         DBG_DEBUG("deleted [0x%"PRIx64"] [%s]\n", entry->ino, entry->path);
447         return 0;
448 }
449
450 /**
451  * Add result to inode->path mapping dbwrap rbt db
452  *
453  * This is necessary as a CNID db substitute, ie we need a way to
454  * simulate unique, constant numerical identifiers for paths with an
455  * API that supports mapping from id to path.
456  *
457  * Entries are talloc'ed of the query, using talloc_reference() if
458  * multiple queries returned the same result. That way we can cleanup
459  * entries by calling talloc_free() on the query slq handles.
460  **/
461
462 static bool inode_map_add(struct sl_query *slq,
463                           uint64_t ino,
464                           const char *path,
465                           struct stat_ex *st)
466 {
467         NTSTATUS status;
468         struct sl_inode_path_map *entry;
469         TDB_DATA key, value;
470         void *p;
471
472         key = make_tdb_data((uint8_t *)&ino, sizeof(ino));
473         status = dbwrap_fetch(slq->mds_ctx->ino_path_map, slq, key, &value);
474
475         if (NT_STATUS_IS_OK(status)) {
476                 /*
477                  * We have one db, so when different parallel queries
478                  * return the same file, we have to refcount entries
479                  * in the db.
480                  */
481
482                 if (value.dsize != sizeof(void *)) {
483                         DEBUG(1, ("invalid dsize\n"));
484                         return false;
485                 }
486                 memcpy(&p, value.dptr, sizeof(p));
487                 entry = talloc_get_type_abort(p, struct sl_inode_path_map);
488
489                 DEBUG(10, ("map: %s\n", entry->path));
490
491                 entry = talloc_reference(slq->entries_ctx, entry);
492                 if (entry == NULL) {
493                         DEBUG(1, ("talloc_reference failed\n"));
494                         return false;
495                 }
496                 return true;
497         }
498
499         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
500                 DEBUG(1, ("dbwrap_fetch failed %s\n", nt_errstr(status)));
501                 return false;
502         }
503
504         entry = talloc_zero(slq->entries_ctx, struct sl_inode_path_map);
505         if (entry == NULL) {
506                 DEBUG(1, ("talloc failed\n"));
507                 return false;
508         }
509
510         entry->ino = ino;
511         entry->mds_ctx = slq->mds_ctx;
512         entry->st = *st;
513         entry->path = talloc_strdup(entry, path);
514         if (entry->path == NULL) {
515                 DEBUG(1, ("talloc failed\n"));
516                 TALLOC_FREE(entry);
517                 return false;
518         }
519
520         status = dbwrap_store(slq->mds_ctx->ino_path_map, key,
521                               make_tdb_data((void *)&entry, sizeof(void *)), 0);
522         if (!NT_STATUS_IS_OK(status)) {
523                 DEBUG(1, ("Failed to store record: %s\n", nt_errstr(status)));
524                 TALLOC_FREE(entry);
525                 return false;
526         }
527
528         talloc_set_destructor(entry, ino_path_map_destr_cb);
529
530         return true;
531 }
532
533 bool mds_add_result(struct sl_query *slq, const char *path)
534 {
535         struct smb_filename *smb_fname = NULL;
536         const char *relative = NULL;
537         char *fake_path = NULL;
538         struct stat_ex sb;
539         uint64_t ino64;
540         int result;
541         NTSTATUS status;
542         bool sub;
543         bool ok;
544
545         /*
546          * We're in a tevent callback which means in the case of
547          * running as external RPC service we're running as root and
548          * not as the user.
549          */
550         if (!become_authenticated_pipe_user(slq->mds_ctx->pipe_session_info)) {
551                 DBG_ERR("can't become authenticated user: %d\n",
552                         slq->mds_ctx->uid);
553                 smb_panic("can't become authenticated user");
554         }
555
556         if (geteuid() != slq->mds_ctx->uid) {
557                 DBG_ERR("uid mismatch: %d/%d\n", geteuid(), slq->mds_ctx->uid);
558                 smb_panic("uid mismatch");
559         }
560
561         /*
562          * We've changed identity to the authenticated pipe user, so
563          * any function exit below must ensure we switch back
564          */
565
566         status = synthetic_pathref(talloc_tos(),
567                                    slq->mds_ctx->conn->cwd_fsp,
568                                    path,
569                                    NULL,
570                                    NULL,
571                                    0,
572                                    0,
573                                    &smb_fname);
574         if (!NT_STATUS_IS_OK(status)) {
575                 DBG_DEBUG("synthetic_pathref [%s]: %s\n",
576                           smb_fname_str_dbg(smb_fname),
577                           nt_errstr(status));
578                 unbecome_authenticated_pipe_user();
579                 return true;
580         }
581
582         sb = smb_fname->st;
583
584         status = smbd_check_access_rights_fsp(slq->mds_ctx->conn->cwd_fsp,
585                                               smb_fname->fsp,
586                                               false,
587                                               FILE_READ_DATA);
588         unbecome_authenticated_pipe_user();
589         if (!NT_STATUS_IS_OK(status)) {
590                 TALLOC_FREE(smb_fname);
591                 return true;
592         }
593
594         /* Done with smb_fname now. */
595         TALLOC_FREE(smb_fname);
596
597         ino64 = SMB_VFS_FS_FILE_ID(slq->mds_ctx->conn, &sb);
598
599         if (slq->cnids) {
600                 bool found;
601
602                 /*
603                  * Check whether the found element is in the requested
604                  * set of IDs. Note that we're faking CNIDs by using
605                  * filesystem inode numbers here
606                  */
607                 found = bsearch(&ino64,
608                                 slq->cnids,
609                                 slq->cnids_num,
610                                 sizeof(uint64_t),
611                                 cnid_comp_fn);
612                 if (!found) {
613                         return true;
614                 }
615         }
616
617         sub = subdir_of(slq->mds_ctx->spath,
618                         slq->mds_ctx->spath_len,
619                         path,
620                         &relative);
621         if (!sub) {
622                 DBG_ERR("[%s] is not inside [%s]\n",
623                         path, slq->mds_ctx->spath);
624                 slq->state = SLQ_STATE_ERROR;
625                 return false;
626         }
627
628         /*
629          * Add inode number and filemeta to result set, this is what
630          * we return as part of the result set of a query
631          */
632         result = dalloc_add_copy(slq->query_results->cnids->ca_cnids,
633                                  &ino64,
634                                  uint64_t);
635         if (result != 0) {
636                 DBG_ERR("dalloc error\n");
637                 slq->state = SLQ_STATE_ERROR;
638                 return false;
639         }
640
641         fake_path = talloc_asprintf(slq,
642                                     "/%s/%s",
643                                     slq->mds_ctx->sharename,
644                                     relative);
645         if (fake_path == NULL) {
646                 slq->state = SLQ_STATE_ERROR;
647                 return false;
648         }
649
650         ok = add_filemeta(slq->mds_ctx,
651                           slq->reqinfo,
652                           slq->query_results->fm_array,
653                           fake_path,
654                           &sb);
655         if (!ok) {
656                 DBG_ERR("add_filemeta error\n");
657                 TALLOC_FREE(fake_path);
658                 slq->state = SLQ_STATE_ERROR;
659                 return false;
660         }
661
662         ok = inode_map_add(slq, ino64, fake_path, &sb);
663         TALLOC_FREE(fake_path);
664         if (!ok) {
665                 DEBUG(1, ("inode_map_add error\n"));
666                 slq->state = SLQ_STATE_ERROR;
667                 return false;
668         }
669
670         slq->query_results->num_results++;
671         return true;
672 }
673
674 /***********************************************************
675  * Spotlight RPC functions
676  ***********************************************************/
677
678 static bool slrpc_fetch_properties(struct mds_ctx *mds_ctx,
679                                    const DALLOC_CTX *query, DALLOC_CTX *reply)
680 {
681         sl_dict_t *dict;
682         sl_array_t *array;
683         char *s;
684         uint64_t u;
685         sl_bool_t b;
686         sl_uuid_t uuid;
687         int result;
688
689         dict = dalloc_zero(reply, sl_dict_t);
690         if (dict == NULL) {
691                 return false;
692         }
693
694         /* kMDSStoreHasPersistentUUID = false */
695         result = dalloc_stradd(dict, "kMDSStoreHasPersistentUUID");
696         if (result != 0) {
697                 return false;
698         }
699         b = false;
700         result = dalloc_add_copy(dict, &b, sl_bool_t);
701         if (result != 0) {
702                 return false;
703         }
704
705         /* kMDSStoreIsBackup = false */
706         result = dalloc_stradd(dict, "kMDSStoreIsBackup");
707         if (result != 0) {
708                 return false;
709         }
710         b = false;
711         result = dalloc_add_copy(dict, &b, sl_bool_t);
712         if (result != 0) {
713                 return false;
714         }
715
716         /* kMDSStoreUUID = uuid */
717         result = dalloc_stradd(dict, "kMDSStoreUUID");
718         if (result != 0) {
719                 return false;
720         }
721         memcpy(uuid.sl_uuid, "fakeuuidfakeuuid", sizeof(uuid.sl_uuid));
722         result = dalloc_add_copy(dict, &uuid, sl_uuid_t);
723         if (result != 0) {
724                 return false;
725         }
726
727         /* kMDSStoreSupportsVolFS = true */
728         result = dalloc_stradd(dict, "kMDSStoreSupportsVolFS");
729         if (result != 0) {
730                 return false;
731         }
732         b = true;
733         result = dalloc_add_copy(dict, &b, sl_bool_t);
734         if (result != 0) {
735                 return false;
736         }
737
738         /* kMDSVolumeUUID = uuid */
739         result = dalloc_stradd(dict, "kMDSVolumeUUID");
740         if (result != 0) {
741                 return false;
742         }
743         memcpy(uuid.sl_uuid, "fakeuuidfakeuuid", sizeof(uuid.sl_uuid));
744         result = dalloc_add_copy(dict, &uuid, sl_uuid_t);
745         if (result != 0) {
746                 return false;
747         }
748
749         /* kMDSDiskStoreSpindleNumber = 1 (fake) */
750         result = dalloc_stradd(dict, "kMDSDiskStoreSpindleNumber");
751         if (result != 0) {
752                 return false;
753         }
754         u = 1;
755         result = dalloc_add_copy(dict, &u, uint64_t);
756         if (result != 0) {
757                 return false;
758         }
759
760         /* kMDSDiskStorePolicy = 3 (whatever that means, taken from OS X) */
761         result = dalloc_stradd(dict, "kMDSDiskStorePolicy");
762         if (result != 0) {
763                 return false;
764         }
765         u = 3;
766         result = dalloc_add_copy(dict, &u, uint64_t);
767         if (result != 0) {
768                 return false;
769         }
770
771         /* kMDSStoreMetaScopes array */
772         result = dalloc_stradd(dict, "kMDSStoreMetaScopes");
773         if (result != 0) {
774                 return false;
775         }
776         array = dalloc_zero(dict, sl_array_t);
777         if (array == NULL) {
778                 return NULL;
779         }
780         result = dalloc_stradd(array, "kMDQueryScopeComputer");
781         if (result != 0) {
782                 return false;
783         }
784         result = dalloc_stradd(array, "kMDQueryScopeAllIndexed");
785         if (result != 0) {
786                 return false;
787         }
788         result = dalloc_stradd(array, "kMDQueryScopeComputerIndexed");
789         if (result != 0) {
790                 return false;
791         }
792         result = dalloc_add(dict, array, sl_array_t);
793         if (result != 0) {
794                 return false;
795         }
796
797         /* kMDSStoreDevice = 0x1000003 (whatever that means, taken from OS X) */
798         result = dalloc_stradd(dict, "kMDSStoreDevice");
799         if (result != 0) {
800                 return false;
801         }
802         u = 0x1000003;
803         result = dalloc_add_copy(dict, &u, uint64_t);
804         if (result != 0) {
805                 return false;
806         }
807
808         /* kMDSStoreSupportsTCC = true (whatever that means, taken from OS X) */
809         result = dalloc_stradd(dict, "kMDSStoreSupportsTCC");
810         if (result != 0) {
811                 return false;
812         }
813         b = true;
814         result = dalloc_add_copy(dict, &b, sl_bool_t);
815         if (result != 0) {
816                 return false;
817         }
818
819         /* kMDSStorePathScopes = ["/"] (whatever that means, taken from OS X) */
820         result = dalloc_stradd(dict, "kMDSStorePathScopes");
821         if (result != 0) {
822                 return false;
823         }
824         array = dalloc_zero(dict, sl_array_t);
825         if (array == NULL) {
826                 return false;
827         }
828         s = talloc_strdup(dict, "/");
829         if (s == NULL) {
830                 return false;
831         }
832         talloc_set_name(s, "smb_ucs2_t *");
833         result = dalloc_add(array, s, smb_ucs2_t *);
834         if (result != 0) {
835                 return false;
836         }
837         result = dalloc_add(dict, array, sl_array_t);
838         if (result != 0) {
839                 return false;
840         }
841
842         result = dalloc_add(reply, dict, sl_dict_t);
843         if (result != 0) {
844                 return false;
845         }
846
847         return true;
848 }
849
850 static void slq_close_timer(struct tevent_context *ev,
851                             struct tevent_timer *te,
852                             struct timeval current_time,
853                             void *private_data)
854 {
855         struct sl_query *slq = talloc_get_type_abort(
856                 private_data, struct sl_query);
857         struct mds_ctx *mds_ctx = slq->mds_ctx;
858
859         SLQ_DEBUG(10, slq, "expired");
860
861         TALLOC_FREE(slq);
862
863         if (CHECK_DEBUGLVL(10)) {
864                 for (slq = mds_ctx->query_list; slq != NULL; slq = slq->next) {
865                         SLQ_DEBUG(10, slq, "pending");
866                 }
867         }
868 }
869
870 /**
871  * Translate a fake scope from the client like /sharename/dir
872  * to the real server-side path, replacing the "/sharename" part
873  * with the absolute server-side path of the share.
874  **/
875 static bool mdssvc_real_scope(struct sl_query *slq, const char *fake_scope)
876 {
877         size_t sname_len = strlen(slq->mds_ctx->sharename);
878         size_t fake_scope_len = strlen(fake_scope);
879
880         if (fake_scope_len < sname_len + 1) {
881                 DBG_ERR("Short scope [%s] for share [%s]\n",
882                         fake_scope, slq->mds_ctx->sharename);
883                 return false;
884         }
885
886         slq->path_scope = talloc_asprintf(slq,
887                                           "%s%s",
888                                           slq->mds_ctx->spath,
889                                           fake_scope + sname_len + 1);
890         if (slq->path_scope == NULL) {
891                 return false;
892         }
893         return true;
894 }
895
896 /**
897  * Begin a search query
898  **/
899 static bool slrpc_open_query(struct mds_ctx *mds_ctx,
900                              const DALLOC_CTX *query, DALLOC_CTX *reply)
901 {
902         bool ok;
903         uint64_t sl_result;
904         uint64_t *uint64p;
905         DALLOC_CTX *reqinfo;
906         sl_array_t *array, *path_scope;
907         sl_cnids_t *cnids;
908         struct sl_query *slq = NULL;
909         int result;
910         const char *querystring = NULL;
911         size_t querystring_len;
912         char *dest = NULL;
913         size_t dest_remaining;
914         size_t nconv;
915         char *scope = NULL;
916
917         array = dalloc_zero(reply, sl_array_t);
918         if (array == NULL) {
919                 return false;
920         }
921
922         /* Allocate and initialize query object */
923         slq = talloc_zero(mds_ctx, struct sl_query);
924         if (slq == NULL) {
925                 return false;
926         }
927         slq->entries_ctx = talloc_named_const(slq, 0, "struct sl_query.entries_ctx");
928         if (slq->entries_ctx == NULL) {
929                 TALLOC_FREE(slq);
930                 return false;
931         }
932         talloc_set_destructor(slq, slq_destructor_cb);
933         slq->state = SLQ_STATE_NEW;
934         slq->mds_ctx = mds_ctx;
935
936         slq->last_used = timeval_current();
937         slq->start_time = slq->last_used;
938         slq->expire_time = timeval_add(&slq->last_used, MAX_SL_RUNTIME, 0);
939         slq->te = tevent_add_timer(global_event_context(), slq,
940                                    slq->expire_time, slq_close_timer, slq);
941         if (slq->te == NULL) {
942                 DEBUG(1, ("tevent_add_timer failed\n"));
943                 goto error;
944         }
945
946         querystring = dalloc_value_for_key(query, "DALLOC_CTX", 0,
947                                            "DALLOC_CTX", 1,
948                                            "kMDQueryString",
949                                            "char *");
950         if (querystring == NULL) {
951                 DEBUG(1, ("missing kMDQueryString\n"));
952                 goto error;
953         }
954
955         querystring_len = talloc_array_length(querystring);
956
957         slq->query_string = talloc_array(slq, char, querystring_len);
958         if (slq->query_string == NULL) {
959                 DEBUG(1, ("out of memory\n"));
960                 goto error;
961         }
962         dest = slq->query_string;
963         dest_remaining = talloc_array_length(dest);
964
965         nconv = smb_iconv(mds_ctx->ic_nfd_to_nfc,
966                           &querystring,
967                           &querystring_len,
968                           &dest,
969                           &dest_remaining);
970         if (nconv == (size_t)-1) {
971                 DBG_ERR("smb_iconv failed for: %s\n", querystring);
972                 return false;
973         }
974
975         uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
976                              "uint64_t", 1);
977         if (uint64p == NULL) {
978                 goto error;
979         }
980         slq->ctx1 = *uint64p;
981         uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
982                              "uint64_t", 2);
983         if (uint64p == NULL) {
984                 goto error;
985         }
986         slq->ctx2 = *uint64p;
987
988         path_scope = dalloc_value_for_key(query, "DALLOC_CTX", 0,
989                                           "DALLOC_CTX", 1,
990                                           "kMDScopeArray",
991                                           "sl_array_t");
992         if (path_scope == NULL) {
993                 DBG_ERR("missing kMDScopeArray\n");
994                 goto error;
995         }
996
997         scope = dalloc_get(path_scope, "char *", 0);
998         if (scope == NULL) {
999                 scope = dalloc_get(path_scope,
1000                                    "DALLOC_CTX", 0,
1001                                    "char *", 0);
1002         }
1003         if (scope == NULL) {
1004                 DBG_ERR("Failed to parse kMDScopeArray\n");
1005                 goto error;
1006         }
1007
1008         ok = mdssvc_real_scope(slq, scope);
1009         if (!ok) {
1010                 goto error;
1011         }
1012
1013         reqinfo = dalloc_value_for_key(query, "DALLOC_CTX", 0,
1014                                        "DALLOC_CTX", 1,
1015                                        "kMDAttributeArray",
1016                                        "sl_array_t");
1017         if (reqinfo == NULL) {
1018                 DBG_ERR("missing kMDAttributeArray\n");
1019                 goto error;
1020         }
1021
1022         slq->reqinfo = talloc_steal(slq, reqinfo);
1023         DEBUG(10, ("requested attributes: %s", dalloc_dump(reqinfo, 0)));
1024
1025         cnids = dalloc_value_for_key(query, "DALLOC_CTX", 0,
1026                                      "DALLOC_CTX", 1,
1027                                      "kMDQueryItemArray",
1028                                      "sl_array_t");
1029         if (cnids) {
1030                 ok = sort_cnids(slq, cnids->ca_cnids);
1031                 if (!ok) {
1032                         goto error;
1033                 }
1034         }
1035
1036         ok = create_result_handle(slq);
1037         if (!ok) {
1038                 DEBUG(1, ("create_result_handle error\n"));
1039                 slq->state = SLQ_STATE_ERROR;
1040                 goto error;
1041         }
1042
1043         SLQ_DEBUG(10, slq, "new");
1044
1045         DLIST_ADD(mds_ctx->query_list, slq);
1046
1047         ok = mds_ctx->backend->search_start(slq);
1048         if (!ok) {
1049                 DBG_ERR("backend search_start failed\n");
1050                 goto error;
1051         }
1052
1053         sl_result = 0;
1054         result = dalloc_add_copy(array, &sl_result, uint64_t);
1055         if (result != 0) {
1056                 goto error;
1057         }
1058         result = dalloc_add(reply, array, sl_array_t);
1059         if (result != 0) {
1060                 goto error;
1061         }
1062         return true;
1063
1064 error:
1065         sl_result = UINT64_MAX;
1066         TALLOC_FREE(slq);
1067         result = dalloc_add_copy(array, &sl_result, uint64_t);
1068         if (result != 0) {
1069                 return false;
1070         }
1071         result = dalloc_add(reply, array, sl_array_t);
1072         if (result != 0) {
1073                 return false;
1074         }
1075         return true;
1076 }
1077
1078 /**
1079  * Fetch results of a query
1080  **/
1081 static bool slrpc_fetch_query_results(struct mds_ctx *mds_ctx,
1082                                       const DALLOC_CTX *query,
1083                                       DALLOC_CTX *reply)
1084 {
1085         bool ok;
1086         struct sl_query *slq = NULL;
1087         uint64_t *uint64p, ctx1, ctx2;
1088         uint64_t status;
1089         sl_array_t *array;
1090         int result;
1091
1092         array = dalloc_zero(reply, sl_array_t);
1093         if (array == NULL) {
1094                 return false;
1095         }
1096
1097         /* Get query for context */
1098         uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1099                              "uint64_t", 1);
1100         if (uint64p == NULL) {
1101                 goto error;
1102         }
1103         ctx1 = *uint64p;
1104
1105         uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1106                              "uint64_t", 2);
1107         if (uint64p == NULL) {
1108                 goto error;
1109         }
1110         ctx2 = *uint64p;
1111
1112         slq = slq_for_ctx(mds_ctx, ctx1, ctx2);
1113         if (slq == NULL) {
1114                 DEBUG(1, ("bad context: [0x%jx,0x%jx]\n",
1115                           (uintmax_t)ctx1, (uintmax_t)ctx2));
1116                 goto error;
1117         }
1118
1119         TALLOC_FREE(slq->te);
1120         slq->last_used = timeval_current();
1121         slq->expire_time = timeval_add(&slq->last_used, MAX_SL_RUNTIME, 0);
1122         slq->te = tevent_add_timer(global_event_context(), slq,
1123                                    slq->expire_time, slq_close_timer, slq);
1124         if (slq->te == NULL) {
1125                 DEBUG(1, ("tevent_add_timer failed\n"));
1126                 goto error;
1127         }
1128
1129         SLQ_DEBUG(10, slq, "fetch");
1130
1131         switch (slq->state) {
1132         case SLQ_STATE_RUNNING:
1133         case SLQ_STATE_RESULTS:
1134         case SLQ_STATE_FULL:
1135         case SLQ_STATE_DONE:
1136                 ok = add_results(array, slq);
1137                 if (!ok) {
1138                         DEBUG(1, ("error adding results\n"));
1139                         goto error;
1140                 }
1141                 if (slq->state == SLQ_STATE_FULL) {
1142                         slq->state = SLQ_STATE_RUNNING;
1143                         slq->mds_ctx->backend->search_cont(slq);
1144                 }
1145                 break;
1146
1147         case SLQ_STATE_ERROR:
1148                 DEBUG(1, ("query in error state\n"));
1149                 goto error;
1150
1151         default:
1152                 DEBUG(1, ("unexpected query state %d\n", slq->state));
1153                 goto error;
1154         }
1155
1156         result = dalloc_add(reply, array, sl_array_t);
1157         if (result != 0) {
1158                 goto error;
1159         }
1160         return true;
1161
1162 error:
1163         status = UINT64_MAX;
1164         TALLOC_FREE(slq);
1165         result = dalloc_add_copy(array, &status, uint64_t);
1166         if (result != 0) {
1167                 return false;
1168         }
1169         result = dalloc_add(reply, array, sl_array_t);
1170         if (result != 0) {
1171                 return false;
1172         }
1173         return true;
1174 }
1175
1176 /**
1177  * Store metadata attributes for a CNID
1178  **/
1179 static bool slrpc_store_attributes(struct mds_ctx *mds_ctx,
1180                                    const DALLOC_CTX *query, DALLOC_CTX *reply)
1181 {
1182         uint64_t sl_result;
1183         sl_array_t *array;
1184         int result;
1185
1186         array = dalloc_zero(reply, sl_array_t);
1187         if (array == NULL) {
1188                 return false;
1189         }
1190
1191         /*
1192          * FIXME: not implemented. Used by the client for eg setting
1193          * the modification date of the shared directory which clients
1194          * poll indicating changes on the share and cause the client
1195          * to refresh view.
1196          */
1197
1198         sl_result = 0;
1199         result = dalloc_add_copy(array, &sl_result, uint64_t);
1200         if (result != 0) {
1201                 return false;
1202         }
1203         result = dalloc_add(reply, array, sl_array_t);
1204         if (result != 0) {
1205                 return false;
1206         }
1207
1208         return true;
1209 }
1210
1211 /**
1212  * Fetch supported metadata attributes for a CNID
1213  **/
1214 static bool slrpc_fetch_attributenames(struct mds_ctx *mds_ctx,
1215                                        const DALLOC_CTX *query,
1216                                        DALLOC_CTX *reply)
1217 {
1218         uint64_t id;
1219         sl_cnids_t *cnids;
1220         sl_array_t *array;
1221         uint64_t sl_result;
1222         sl_cnids_t *replycnids;
1223         sl_array_t *mdattrs;
1224         sl_filemeta_t *fmeta;
1225         int result;
1226         void *p;
1227
1228         cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 1);
1229         if (cnids == NULL) {
1230                 return false;
1231         }
1232
1233         p = dalloc_get_object(cnids->ca_cnids, 0);
1234         if (p == NULL) {
1235                 return NULL;
1236         }
1237         memcpy(&id, p, sizeof(uint64_t));
1238
1239         /* Result array */
1240         array = dalloc_zero(reply, sl_array_t);
1241         if (array == NULL) {
1242                 return false;
1243         }
1244
1245         result = dalloc_add(reply, array, sl_array_t);
1246         if (result != 0) {
1247                 return false;
1248         }
1249
1250         /* Return result value 0 */
1251         sl_result = 0;
1252         result = dalloc_add_copy(array, &sl_result, uint64_t);
1253         if (result != 0) {
1254                 return false;
1255         }
1256
1257         /* Return CNID array */
1258         replycnids = talloc_zero(reply, sl_cnids_t);
1259         if (replycnids == NULL) {
1260                 return false;
1261         }
1262
1263         replycnids->ca_cnids = dalloc_new(cnids);
1264         if (replycnids->ca_cnids == NULL) {
1265                 return false;
1266         }
1267
1268         replycnids->ca_unkn1 = 0xfec;
1269         replycnids->ca_context = cnids->ca_context;
1270         result = dalloc_add_copy(replycnids->ca_cnids, &id, uint64_t);
1271         if (result != 0) {
1272                 return false;
1273         }
1274         result = dalloc_add(array, replycnids, sl_cnids_t);
1275         if (result != 0) {
1276                 return false;
1277         }
1278
1279         /*
1280          * FIXME: this should return the real attributes from all
1281          * known metadata sources (Tracker and filesystem)
1282          */
1283         mdattrs = dalloc_zero(reply, sl_array_t);
1284         if (mdattrs == NULL) {
1285                 return false;
1286         }
1287
1288         result = dalloc_stradd(mdattrs, "kMDItemFSName");
1289         if (result != 0) {
1290                 return false;
1291         }
1292         result = dalloc_stradd(mdattrs, "kMDItemDisplayName");
1293         if (result != 0) {
1294                 return false;
1295         }
1296         result = dalloc_stradd(mdattrs, "kMDItemFSSize");
1297         if (result != 0) {
1298                 return false;
1299         }
1300         result = dalloc_stradd(mdattrs, "kMDItemFSOwnerUserID");
1301         if (result != 0) {
1302                 return false;
1303         }
1304         result = dalloc_stradd(mdattrs, "kMDItemFSOwnerGroupID");
1305         if (result != 0) {
1306                 return false;
1307         }
1308         result = dalloc_stradd(mdattrs, "kMDItemFSContentChangeDate");
1309         if (result != 0) {
1310                 return false;
1311         }
1312
1313         fmeta = dalloc_zero(reply, sl_filemeta_t);
1314         if (fmeta == NULL) {
1315                 return false;
1316         }
1317         result = dalloc_add(fmeta, mdattrs, sl_array_t);
1318         if (result != 0) {
1319                 return false;
1320         }
1321         result = dalloc_add(array, fmeta, sl_filemeta_t);
1322         if (result != 0) {
1323                 return false;
1324         }
1325
1326         return true;
1327 }
1328
1329 /**
1330  * Fetch metadata attribute values for a CNID
1331  **/
1332 static bool slrpc_fetch_attributes(struct mds_ctx *mds_ctx,
1333                                    const DALLOC_CTX *query, DALLOC_CTX *reply)
1334 {
1335         int result;
1336         bool ok;
1337         sl_array_t *array;
1338         sl_cnids_t *cnids;
1339         sl_cnids_t *replycnids;
1340         sl_array_t *reqinfo;
1341         uint64_t ino;
1342         uint64_t sl_result;
1343         sl_filemeta_t *fm;
1344         sl_array_t *fm_array;
1345         sl_nil_t nil;
1346         char *path = NULL;
1347         struct smb_filename *smb_fname = NULL;
1348         struct stat_ex *sp = NULL;
1349         struct sl_inode_path_map *elem = NULL;
1350         void *p;
1351         TDB_DATA val = tdb_null;
1352         NTSTATUS status;
1353
1354         array = dalloc_zero(reply, sl_array_t);
1355         if (array == NULL) {
1356                 return false;
1357         }
1358         replycnids = talloc_zero(reply, sl_cnids_t);
1359         if (replycnids == NULL) {
1360                 goto error;
1361         }
1362         replycnids->ca_cnids = dalloc_new(replycnids);
1363         if (replycnids->ca_cnids == NULL) {
1364                 goto error;
1365         }
1366         fm = dalloc_zero(array, sl_filemeta_t);
1367         if (fm == NULL) {
1368                 goto error;
1369         }
1370         fm_array = dalloc_zero(fm, sl_array_t);
1371         if (fm_array == NULL) {
1372                 goto error;
1373         }
1374         /* For some reason the list of results always starts with a nil entry */
1375         result = dalloc_add_copy(fm_array, &nil, sl_nil_t);
1376         if (result == -1) {
1377                 goto error;
1378         }
1379
1380         reqinfo = dalloc_get(query, "DALLOC_CTX", 0, "sl_array_t", 1);
1381         if (reqinfo == NULL) {
1382                 goto error;
1383         }
1384
1385         cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 2);
1386         if (cnids == NULL) {
1387                 goto error;
1388         }
1389         p = dalloc_get_object(cnids->ca_cnids, 0);
1390         if (p == NULL) {
1391                 goto error;
1392         }
1393         memcpy(&ino, p, sizeof(uint64_t));
1394
1395         replycnids->ca_unkn1 = 0xfec;
1396         replycnids->ca_context = cnids->ca_context;
1397         result = dalloc_add_copy(replycnids->ca_cnids, &ino, uint64_t);
1398         if (result != 0) {
1399                 goto error;
1400         }
1401
1402         status = dbwrap_fetch(mds_ctx->ino_path_map, reply,
1403                               make_tdb_data((void*)&ino, sizeof(uint64_t)),
1404                               &val);
1405         if (NT_STATUS_IS_OK(status)) {
1406                 if (val.dsize != sizeof(p)) {
1407                         DBG_ERR("invalid record pointer size: %zd\n", val.dsize);
1408                         TALLOC_FREE(val.dptr);
1409                         goto error;
1410                 }
1411
1412                 memcpy(&p, val.dptr, sizeof(p));
1413                 elem = talloc_get_type_abort(p, struct sl_inode_path_map);
1414                 path = elem->path;
1415
1416                 sp = &elem->st;
1417         }
1418
1419         ok = add_filemeta(mds_ctx, reqinfo, fm_array, path, sp);
1420         if (!ok) {
1421                 goto error;
1422         }
1423
1424         sl_result = 0;
1425         result = dalloc_add_copy(array, &sl_result, uint64_t);
1426         if (result != 0) {
1427                 goto error;
1428         }
1429         result = dalloc_add(array, replycnids, sl_cnids_t);
1430         if (result != 0) {
1431                 goto error;
1432         }
1433         result = dalloc_add(fm, fm_array, sl_array_t);
1434         if (result != 0) {
1435                 goto error;
1436         }
1437         result = dalloc_add(array, fm, sl_filemeta_t);
1438         if (result != 0) {
1439                 goto error;
1440         }
1441         result = dalloc_add(reply, array, sl_array_t);
1442         if (result != 0) {
1443                 goto error;
1444         }
1445
1446         TALLOC_FREE(smb_fname);
1447         return true;
1448
1449 error:
1450
1451         TALLOC_FREE(smb_fname);
1452         sl_result = UINT64_MAX;
1453         result = dalloc_add_copy(array, &sl_result, uint64_t);
1454         if (result != 0) {
1455                 return false;
1456         }
1457         result = dalloc_add(reply, array, sl_array_t);
1458         if (result != 0) {
1459                 return false;
1460         }
1461
1462         return true;
1463 }
1464
1465 /**
1466  * Close a query
1467  **/
1468 static bool slrpc_close_query(struct mds_ctx *mds_ctx,
1469                               const DALLOC_CTX *query, DALLOC_CTX *reply)
1470 {
1471         struct sl_query *slq = NULL;
1472         uint64_t *uint64p, ctx1, ctx2;
1473         sl_array_t *array;
1474         uint64_t sl_res;
1475         int result;
1476
1477         array = dalloc_zero(reply, sl_array_t);
1478         if (array == NULL) {
1479                 return false;
1480         }
1481
1482         /* Context */
1483         uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1484                              "uint64_t", 1);
1485         if (uint64p == NULL) {
1486                 goto done;
1487         }
1488         ctx1 = *uint64p;
1489
1490         uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1491                              "uint64_t", 2);
1492         if (uint64p == NULL) {
1493                 goto done;
1494         }
1495         ctx2 = *uint64p;
1496
1497         /* Get query for context and free it */
1498         slq = slq_for_ctx(mds_ctx, ctx1, ctx2);
1499         if (slq == NULL) {
1500                 DEBUG(1, ("bad context: [0x%jx,0x%jx]\n",
1501                           (uintmax_t)ctx1, (uintmax_t)ctx2));
1502                 goto done;
1503         }
1504
1505         SLQ_DEBUG(10, slq, "close");
1506         TALLOC_FREE(slq);
1507
1508 done:
1509         sl_res = UINT64_MAX;
1510         result = dalloc_add_copy(array, &sl_res, uint64_t);
1511         if (result != 0) {
1512                 return false;
1513         }
1514         result = dalloc_add(reply, array, sl_array_t);
1515         if (result != 0) {
1516                 return false;
1517         }
1518         return true;
1519 }
1520
1521 static struct mdssvc_ctx *mdssvc_init(struct tevent_context *ev)
1522 {
1523         bool ok;
1524
1525         if (mdssvc_ctx != NULL) {
1526                 return mdssvc_ctx;
1527         }
1528
1529         mdssvc_ctx = talloc_zero(ev, struct mdssvc_ctx);
1530         if (mdssvc_ctx == NULL) {
1531                 return NULL;
1532         }
1533
1534         mdssvc_ctx->ev_ctx = ev;
1535
1536         ok = mdsscv_backend_noindex.init(mdssvc_ctx);
1537         if (!ok) {
1538                 DBG_ERR("backend init failed\n");
1539                 TALLOC_FREE(mdssvc_ctx);
1540                 return NULL;
1541         }
1542
1543 #ifdef HAVE_SPOTLIGHT_BACKEND_ES
1544         ok = mdsscv_backend_es.init(mdssvc_ctx);
1545         if (!ok) {
1546                 DBG_ERR("backend init failed\n");
1547                 TALLOC_FREE(mdssvc_ctx);
1548                 return NULL;
1549         }
1550 #endif
1551
1552 #ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
1553         ok = mdsscv_backend_tracker.init(mdssvc_ctx);
1554         if (!ok) {
1555                 DBG_ERR("backend init failed\n");
1556                 TALLOC_FREE(mdssvc_ctx);
1557                 return NULL;
1558         }
1559 #endif
1560
1561         return mdssvc_ctx;
1562 }
1563
1564 /**
1565  * Init callbacks at startup
1566  *
1567  * This gets typically called in the main parent smbd which means we can't
1568  * initialize our global state here.
1569  **/
1570 bool mds_init(struct messaging_context *msg_ctx)
1571 {
1572         return true;
1573 }
1574
1575 bool mds_shutdown(void)
1576 {
1577         bool ok;
1578
1579         if (mdssvc_ctx == NULL) {
1580                 return false;
1581         }
1582
1583         ok = mdsscv_backend_noindex.shutdown(mdssvc_ctx);
1584         if (!ok) {
1585                 goto fail;
1586         }
1587
1588 #ifdef HAVE_SPOTLIGHT_BACKEND_ES
1589         ok = mdsscv_backend_es.shutdown(mdssvc_ctx);
1590         if (!ok) {
1591                 goto fail;
1592         }
1593 #endif
1594
1595 #ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
1596         ok = mdsscv_backend_tracker.shutdown(mdssvc_ctx);
1597         if (!ok) {
1598                 goto fail;
1599         }
1600 #endif
1601
1602         ok = true;
1603 fail:
1604         TALLOC_FREE(mdssvc_ctx);
1605         return ok;
1606 }
1607
1608 /**
1609  * Tear down connections and free all resources
1610  **/
1611 static int mds_ctx_destructor_cb(struct mds_ctx *mds_ctx)
1612 {
1613         /*
1614          * We need to free query_list before ino_path_map
1615          */
1616         while (mds_ctx->query_list != NULL) {
1617                 /*
1618                  * slq destructor removes element from list.
1619                  * Don't use TALLOC_FREE()!
1620                  */
1621                 talloc_free(mds_ctx->query_list);
1622         }
1623         TALLOC_FREE(mds_ctx->ino_path_map);
1624
1625         if (mds_ctx->conn != NULL) {
1626                 SMB_VFS_DISCONNECT(mds_ctx->conn);
1627                 conn_free(mds_ctx->conn);
1628         }
1629
1630         ZERO_STRUCTP(mds_ctx);
1631
1632         return 0;
1633 }
1634
1635 /**
1636  * Initialise a context per RPC bind
1637  *
1638  * This ends up being called for every tcon, because the client does a
1639  * RPC bind for every tcon, so this is acually a per tcon context.
1640  **/
1641 NTSTATUS mds_init_ctx(TALLOC_CTX *mem_ctx,
1642                       struct tevent_context *ev,
1643                       struct messaging_context *msg_ctx,
1644                       struct auth_session_info *session_info,
1645                       int snum,
1646                       const char *sharename,
1647                       const char *path,
1648                       struct mds_ctx **_mds_ctx)
1649 {
1650         const struct loadparm_substitution *lp_sub =
1651                 loadparm_s3_global_substitution();
1652         struct smb_filename conn_basedir;
1653         struct mds_ctx *mds_ctx;
1654         int backend;
1655         int ret;
1656         bool ok;
1657         smb_iconv_t iconv_hnd = (smb_iconv_t)-1;
1658         NTSTATUS status;
1659
1660         if (!lp_spotlight(snum)) {
1661                 return NT_STATUS_WRONG_VOLUME;
1662         }
1663
1664         mds_ctx = talloc_zero(mem_ctx, struct mds_ctx);
1665         if (mds_ctx == NULL) {
1666                 return NT_STATUS_NO_MEMORY;
1667         }
1668         talloc_set_destructor(mds_ctx, mds_ctx_destructor_cb);
1669
1670         mds_ctx->mdssvc_ctx = mdssvc_init(ev);
1671         if (mds_ctx->mdssvc_ctx == NULL) {
1672                 return NT_STATUS_NO_MEMORY;
1673         }
1674
1675         backend = lp_spotlight_backend(snum);
1676         switch (backend) {
1677         case SPOTLIGHT_BACKEND_NOINDEX:
1678                 mds_ctx->backend = &mdsscv_backend_noindex;
1679                 break;
1680
1681 #ifdef HAVE_SPOTLIGHT_BACKEND_ES
1682         case SPOTLIGHT_BACKEND_ES:
1683                 mds_ctx->backend = &mdsscv_backend_es;
1684                 break;
1685 #endif
1686
1687 #ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
1688         case SPOTLIGHT_BACKEND_TRACKER:
1689                 mds_ctx->backend = &mdsscv_backend_tracker;
1690                 break;
1691 #endif
1692         default:
1693                 DBG_ERR("Unknown backend %d\n", backend);
1694                 TALLOC_FREE(mdssvc_ctx);
1695                 status = NT_STATUS_INTERNAL_ERROR;
1696                 goto error;
1697         }
1698
1699         iconv_hnd = smb_iconv_open_ex(mds_ctx,
1700                                       "UTF8-NFD",
1701                                       "UTF8-NFC",
1702                                       false);
1703         if (iconv_hnd == (smb_iconv_t)-1) {
1704                 status = NT_STATUS_INTERNAL_ERROR;
1705                 goto error;
1706         }
1707         mds_ctx->ic_nfc_to_nfd = iconv_hnd;
1708
1709         iconv_hnd = smb_iconv_open_ex(mds_ctx,
1710                                       "UTF8-NFC",
1711                                       "UTF8-NFD",
1712                                       false);
1713         if (iconv_hnd == (smb_iconv_t)-1) {
1714                 status = NT_STATUS_INTERNAL_ERROR;
1715                 goto error;
1716         }
1717         mds_ctx->ic_nfd_to_nfc = iconv_hnd;
1718
1719         mds_ctx->sharename = talloc_strdup(mds_ctx, sharename);
1720         if (mds_ctx->sharename == NULL) {
1721                 status = NT_STATUS_NO_MEMORY;
1722                 goto error;
1723         }
1724
1725         mds_ctx->spath = talloc_strdup(mds_ctx, path);
1726         if (mds_ctx->spath == NULL) {
1727                 status = NT_STATUS_NO_MEMORY;
1728                 goto error;
1729         }
1730         mds_ctx->spath_len = strlen(path);
1731
1732         mds_ctx->snum = snum;
1733         mds_ctx->pipe_session_info = session_info;
1734
1735         if (session_info->security_token->num_sids < 1) {
1736                 status = NT_STATUS_BAD_LOGON_SESSION_STATE;
1737                 goto error;
1738         }
1739         sid_copy(&mds_ctx->sid, &session_info->security_token->sids[0]);
1740         mds_ctx->uid = session_info->unix_token->uid;
1741
1742         mds_ctx->ino_path_map = db_open_rbt(mds_ctx);
1743         if (mds_ctx->ino_path_map == NULL) {
1744                 DEBUG(1,("open inode map db failed\n"));
1745                 status = NT_STATUS_INTERNAL_ERROR;
1746                 goto error;
1747         }
1748
1749         status = create_conn_struct_cwd(mds_ctx,
1750                                         ev,
1751                                         msg_ctx,
1752                                         session_info,
1753                                         snum,
1754                                         lp_path(talloc_tos(), lp_sub, snum),
1755                                         &mds_ctx->conn);
1756         if (!NT_STATUS_IS_OK(status)) {
1757                 DBG_ERR("failed to create conn for vfs: %s\n",
1758                         nt_errstr(status));
1759                 goto error;
1760         }
1761
1762         conn_basedir = (struct smb_filename) {
1763                 .base_name = mds_ctx->conn->connectpath,
1764         };
1765
1766         ret = vfs_ChDir(mds_ctx->conn, &conn_basedir);
1767         if (ret != 0) {
1768                 DBG_ERR("vfs_ChDir [%s] failed: %s\n",
1769                         conn_basedir.base_name, strerror(errno));
1770                 status = map_nt_error_from_unix(errno);
1771                 goto error;
1772         }
1773
1774         ok = mds_ctx->backend->connect(mds_ctx);
1775         if (!ok) {
1776                 DBG_ERR("backend connect failed\n");
1777                 status = NT_STATUS_CONNECTION_RESET;
1778                 goto error;
1779         }
1780
1781         *_mds_ctx = mds_ctx;
1782         return NT_STATUS_OK;
1783
1784 error:
1785         if (mds_ctx->ic_nfc_to_nfd != NULL) {
1786                 smb_iconv_close(mds_ctx->ic_nfc_to_nfd);
1787         }
1788         if (mds_ctx->ic_nfd_to_nfc != NULL) {
1789                 smb_iconv_close(mds_ctx->ic_nfd_to_nfc);
1790         }
1791
1792         TALLOC_FREE(mds_ctx);
1793         return status;
1794 }
1795
1796 /**
1797  * Dispatch a Spotlight RPC command
1798  **/
1799 bool mds_dispatch(struct mds_ctx *mds_ctx,
1800                   struct mdssvc_blob *request_blob,
1801                   struct mdssvc_blob *response_blob,
1802                   size_t max_fragment_size)
1803 {
1804         bool ok;
1805         int ret;
1806         DALLOC_CTX *query = NULL;
1807         DALLOC_CTX *reply = NULL;
1808         char *rpccmd;
1809         const struct slrpc_cmd *slcmd;
1810         const struct smb_filename conn_basedir = {
1811                 .base_name = mds_ctx->conn->connectpath,
1812         };
1813         NTSTATUS status;
1814
1815         if (CHECK_DEBUGLVL(10)) {
1816                 const struct sl_query *slq;
1817
1818                 for (slq = mds_ctx->query_list; slq != NULL; slq = slq->next) {
1819                         SLQ_DEBUG(10, slq, "pending");
1820                 }
1821         }
1822
1823         response_blob->length = 0;
1824
1825         DEBUG(10, ("share path: %s\n", mds_ctx->spath));
1826
1827         query = dalloc_new(mds_ctx);
1828         if (query == NULL) {
1829                 ok = false;
1830                 goto cleanup;
1831         }
1832         reply = dalloc_new(mds_ctx);
1833         if (reply == NULL) {
1834                 ok = false;
1835                 goto cleanup;
1836         }
1837
1838         ok = sl_unpack(query, (char *)request_blob->spotlight_blob,
1839                        request_blob->length);
1840         if (!ok) {
1841                 DEBUG(1, ("error unpacking Spotlight RPC blob\n"));
1842                 goto cleanup;
1843         }
1844
1845         DEBUG(5, ("%s", dalloc_dump(query, 0)));
1846
1847         rpccmd = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1848                             "char *", 0);
1849         if (rpccmd == NULL) {
1850                 DEBUG(1, ("missing primary Spotlight RPC command\n"));
1851                 ok = false;
1852                 goto cleanup;
1853         }
1854
1855         DEBUG(10, ("Spotlight RPC cmd: %s\n", rpccmd));
1856
1857         slcmd = slrpc_cmd_by_name(rpccmd);
1858         if (slcmd == NULL) {
1859                 DEBUG(1, ("unsupported primary Spotlight RPC command %s\n",
1860                           rpccmd));
1861                 ok = false;
1862                 goto cleanup;
1863         }
1864
1865         ret = vfs_ChDir(mds_ctx->conn, &conn_basedir);
1866         if (ret != 0) {
1867                 DBG_ERR("vfs_ChDir [%s] failed: %s\n",
1868                         conn_basedir.base_name, strerror(errno));
1869                 ok = false;
1870                 goto cleanup;
1871         }
1872
1873         ok = slcmd->function(mds_ctx, query, reply);
1874         if (!ok) {
1875                 goto cleanup;
1876         }
1877
1878         DBG_DEBUG("%s", dalloc_dump(reply, 0));
1879
1880         status = sl_pack_alloc(response_blob,
1881                                reply,
1882                                response_blob,
1883                                max_fragment_size);
1884         if (!NT_STATUS_IS_OK(status)) {
1885                 DBG_ERR("sl_pack_alloc() failed\n");
1886                 goto cleanup;
1887         }
1888
1889 cleanup:
1890         talloc_free(query);
1891         talloc_free(reply);
1892         return ok;
1893 }