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