mdssvc: better support for search with mdfind from Macs
[Samba.git] / source3 / rpc_server / mdssvc / mdssvc.c
blobc34babb2525bbf37cdf7858c047722ec3b3a121d
1 /*
2 Unix SMB/CIFS implementation.
3 Main metadata server / Spotlight routines
5 Copyright (C) Ralph Boehme 2012-2014
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "includes.h"
22 #include "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"
41 #undef DBGC_CLASS
42 #define DBGC_CLASS DBGC_RPC_SRV
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);
51 struct slq_destroy_state {
52 struct tevent_context *ev;
53 struct sl_query *slq;
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.
60 * The client will bind multiple times to the mdssvc RPC service, once
61 * for every tree connect.
63 static struct mdssvc_ctx *mdssvc_ctx = NULL;
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.
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);
85 /************************************************
86 * Misc utility functions
87 ************************************************/
89 /**
90 * Add requested metadata for a query result element
92 * This could be rewritten to something more sophisticated like
93 * querying metadata from Tracker.
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)
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;
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;
124 return true;
127 meta = dalloc_zero(fm_array, sl_array_t);
128 if (meta == NULL) {
129 return false;
132 nfc_len = strlen(nfc_path);
134 * Simple heuristic, strlen by two should give enough room for NFC to
135 * NFD conversion.
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;
142 dest = nfd_path;
143 dest_remaining = talloc_array_length(dest);
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;
154 for (i = 0; i < metacount; i++) {
155 attribute = dalloc_get_object(reqinfo, i);
156 if (attribute == NULL) {
157 return false;
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;
168 } else if (strcmp(attribute, "kMDItemPath") == 0) {
169 result = dalloc_stradd(meta, nfd_path);
170 if (result != 0) {
171 return false;
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;
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;
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;
191 } else if (strcmp(attribute, "kMDItemFSContentChangeDate") == 0 ||
192 strcmp(attribute, "kMDItemContentModificationDate") == 0)
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;
199 } else {
200 result = dalloc_add_copy(meta, &nil, sl_nil_t);
201 if (result != 0) {
202 return false;
207 result = dalloc_add(fm_array, meta, sl_array_t);
208 if (result != 0) {
209 return false;
211 return true;
214 static int cnid_comp_fn(const void *p1, const void *p2)
216 const uint64_t *cnid1 = p1, *cnid2 = p2;
217 if (*cnid1 == *cnid2) {
218 return 0;
220 if (*cnid1 < *cnid2) {
221 return -1;
223 return 1;
227 * Create a sorted copy of a CNID array
229 static bool sort_cnids(struct sl_query *slq, const DALLOC_CTX *d)
231 uint64_t *cnids = NULL;
232 int i;
233 const void *p;
235 cnids = talloc_array(slq, uint64_t, dalloc_size(d));
236 if (cnids == NULL) {
237 return false;
240 for (i = 0; i < dalloc_size(d); i++) {
241 p = dalloc_get_object(d, i);
242 if (p == NULL) {
243 return NULL;
245 memcpy(&cnids[i], p, sizeof(uint64_t));
247 qsort(cnids, dalloc_size(d), sizeof(uint64_t), cnid_comp_fn);
249 slq->cnids = cnids;
250 slq->cnids_num = dalloc_size(d);
252 return true;
256 * Allocate result handle used in the async Tracker cursor result
257 * handler for storing results
259 static bool create_result_handle(struct sl_query *slq)
261 sl_nil_t nil = 0;
262 struct sl_rslts *query_results;
263 int result;
265 if (slq->query_results) {
266 DEBUG(1, ("unexpected existing result handle\n"));
267 return false;
270 query_results = talloc_zero(slq, struct sl_rslts);
271 if (query_results == NULL) {
272 return false;
275 /* CNIDs */
276 query_results->cnids = talloc_zero(query_results, sl_cnids_t);
277 if (query_results->cnids == NULL) {
278 return false;
280 query_results->cnids->ca_cnids = dalloc_new(query_results->cnids);
281 if (query_results->cnids->ca_cnids == NULL) {
282 return false;
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;
290 query_results->cnids->ca_context = (uint32_t)slq->ctx2;
292 /* FileMeta */
293 query_results->fm_array = dalloc_zero(query_results, sl_array_t);
294 if (query_results->fm_array == NULL) {
295 return false;
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;
304 slq->query_results = query_results;
305 return true;
308 static bool add_results(sl_array_t *array, struct sl_query *slq)
310 sl_filemeta_t *fm;
311 uint64_t status;
312 int result;
313 bool ok;
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.
320 if (slq->state >= SLQ_STATE_DONE) {
321 status = 0;
322 } else {
323 status = 0x23;
326 /* FileMeta */
327 fm = dalloc_zero(array, sl_filemeta_t);
328 if (fm == NULL) {
329 return false;
332 result = dalloc_add_copy(array, &status, uint64_t);
333 if (result != 0) {
334 return false;
336 result = dalloc_add(array, slq->query_results->cnids, sl_cnids_t);
337 if (result != 0) {
338 return false;
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;
346 result = dalloc_add(array, fm, sl_filemeta_t);
347 if (result != 0) {
348 return false;
351 /* This ensure the results get clean up after been sent to the client */
352 talloc_move(array, &slq->query_results);
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;
361 return true;
364 static const struct slrpc_cmd *slrpc_cmd_by_name(const char *rpccmd)
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},
378 for (i = 0; i < ARRAY_SIZE(cmds); i++) {
379 int cmp;
381 cmp = strcmp(cmds[i].name, rpccmd);
382 if (cmp == 0) {
383 return &cmds[i];
387 return NULL;
391 * Search the list of active queries given their context ids
393 static struct sl_query *slq_for_ctx(struct mds_ctx *mds_ctx,
394 uint64_t ctx1, uint64_t ctx2)
396 struct sl_query *q;
398 for (q = mds_ctx->query_list; q; q = q->next) {
399 if ((q->ctx1 == ctx1) && (q->ctx2 == ctx2)) {
400 return q;
404 return NULL;
407 static int slq_destructor_cb(struct sl_query *slq)
409 SLQ_DEBUG(10, slq, "destroying");
411 /* Free all entries before freeing the slq handle! */
412 TALLOC_FREE(slq->entries_ctx);
413 TALLOC_FREE(slq->te);
415 if (slq->mds_ctx != NULL) {
416 DLIST_REMOVE(slq->mds_ctx->query_list, slq);
417 slq->mds_ctx = NULL;
420 TALLOC_FREE(slq->backend_private);
422 return 0;
426 * Remove talloc_refcounted entry from mapping db
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.
433 static int ino_path_map_destr_cb(struct sl_inode_path_map *entry)
435 NTSTATUS status;
436 TDB_DATA key;
438 key = make_tdb_data((uint8_t *)&entry->ino, sizeof(entry->ino));
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;
446 DBG_DEBUG("deleted [0x%"PRIx64"] [%s]\n", entry->ino, entry->path);
447 return 0;
451 * Add result to inode->path mapping dbwrap rbt db
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.
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.
462 static bool inode_map_add(struct sl_query *slq,
463 uint64_t ino,
464 const char *path,
465 struct stat_ex *st)
467 NTSTATUS status;
468 struct sl_inode_path_map *entry;
469 TDB_DATA key, value;
470 void *p;
472 key = make_tdb_data((uint8_t *)&ino, sizeof(ino));
473 status = dbwrap_fetch(slq->mds_ctx->ino_path_map, slq, key, &value);
475 if (NT_STATUS_IS_OK(status)) {
477 * We have one db, so when different parallel queries
478 * return the same file, we have to refcount entries
479 * in the db.
482 if (value.dsize != sizeof(void *)) {
483 DEBUG(1, ("invalid dsize\n"));
484 return false;
486 memcpy(&p, value.dptr, sizeof(p));
487 entry = talloc_get_type_abort(p, struct sl_inode_path_map);
489 DEBUG(10, ("map: %s\n", entry->path));
491 entry = talloc_reference(slq->entries_ctx, entry);
492 if (entry == NULL) {
493 DEBUG(1, ("talloc_reference failed\n"));
494 return false;
496 return true;
499 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
500 DEBUG(1, ("dbwrap_fetch failed %s\n", nt_errstr(status)));
501 return false;
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;
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;
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;
528 talloc_set_destructor(entry, ino_path_map_destr_cb);
530 return true;
533 bool mds_add_result(struct sl_query *slq, const char *path)
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;
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.
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");
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");
562 * We've changed identity to the authenticated pipe user, so
563 * any function exit below must ensure we switch back
566 status = synthetic_pathref(talloc_tos(),
567 slq->mds_ctx->conn->cwd_fsp,
568 path,
569 NULL,
570 NULL,
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;
582 sb = smb_fname->st;
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;
594 /* Done with smb_fname now. */
595 TALLOC_FREE(smb_fname);
597 ino64 = SMB_VFS_FS_FILE_ID(slq->mds_ctx->conn, &sb);
599 if (slq->cnids) {
600 bool found;
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
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;
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;
629 * Add inode number and filemeta to result set, this is what
630 * we return as part of the result set of a query
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;
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;
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;
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;
670 slq->query_results->num_results++;
671 return true;
674 /***********************************************************
675 * Spotlight RPC functions
676 ***********************************************************/
678 static bool slrpc_fetch_properties(struct mds_ctx *mds_ctx,
679 const DALLOC_CTX *query, DALLOC_CTX *reply)
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;
689 dict = dalloc_zero(reply, sl_dict_t);
690 if (dict == NULL) {
691 return false;
694 /* kMDSStoreHasPersistentUUID = false */
695 result = dalloc_stradd(dict, "kMDSStoreHasPersistentUUID");
696 if (result != 0) {
697 return false;
699 b = false;
700 result = dalloc_add_copy(dict, &b, sl_bool_t);
701 if (result != 0) {
702 return false;
705 /* kMDSStoreIsBackup = false */
706 result = dalloc_stradd(dict, "kMDSStoreIsBackup");
707 if (result != 0) {
708 return false;
710 b = false;
711 result = dalloc_add_copy(dict, &b, sl_bool_t);
712 if (result != 0) {
713 return false;
716 /* kMDSStoreUUID = uuid */
717 result = dalloc_stradd(dict, "kMDSStoreUUID");
718 if (result != 0) {
719 return false;
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;
727 /* kMDSStoreSupportsVolFS = true */
728 result = dalloc_stradd(dict, "kMDSStoreSupportsVolFS");
729 if (result != 0) {
730 return false;
732 b = true;
733 result = dalloc_add_copy(dict, &b, sl_bool_t);
734 if (result != 0) {
735 return false;
738 /* kMDSVolumeUUID = uuid */
739 result = dalloc_stradd(dict, "kMDSVolumeUUID");
740 if (result != 0) {
741 return false;
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;
749 /* kMDSDiskStoreSpindleNumber = 1 (fake) */
750 result = dalloc_stradd(dict, "kMDSDiskStoreSpindleNumber");
751 if (result != 0) {
752 return false;
754 u = 1;
755 result = dalloc_add_copy(dict, &u, uint64_t);
756 if (result != 0) {
757 return false;
760 /* kMDSDiskStorePolicy = 3 (whatever that means, taken from OS X) */
761 result = dalloc_stradd(dict, "kMDSDiskStorePolicy");
762 if (result != 0) {
763 return false;
765 u = 3;
766 result = dalloc_add_copy(dict, &u, uint64_t);
767 if (result != 0) {
768 return false;
771 /* kMDSStoreMetaScopes array */
772 result = dalloc_stradd(dict, "kMDSStoreMetaScopes");
773 if (result != 0) {
774 return false;
776 array = dalloc_zero(dict, sl_array_t);
777 if (array == NULL) {
778 return NULL;
780 result = dalloc_stradd(array, "kMDQueryScopeComputer");
781 if (result != 0) {
782 return false;
784 result = dalloc_stradd(array, "kMDQueryScopeAllIndexed");
785 if (result != 0) {
786 return false;
788 result = dalloc_stradd(array, "kMDQueryScopeComputerIndexed");
789 if (result != 0) {
790 return false;
792 result = dalloc_add(dict, array, sl_array_t);
793 if (result != 0) {
794 return false;
797 /* kMDSStoreDevice = 0x1000003 (whatever that means, taken from OS X) */
798 result = dalloc_stradd(dict, "kMDSStoreDevice");
799 if (result != 0) {
800 return false;
802 u = 0x1000003;
803 result = dalloc_add_copy(dict, &u, uint64_t);
804 if (result != 0) {
805 return false;
808 /* kMDSStoreSupportsTCC = true (whatever that means, taken from OS X) */
809 result = dalloc_stradd(dict, "kMDSStoreSupportsTCC");
810 if (result != 0) {
811 return false;
813 b = true;
814 result = dalloc_add_copy(dict, &b, sl_bool_t);
815 if (result != 0) {
816 return false;
819 /* kMDSStorePathScopes = ["/"] (whatever that means, taken from OS X) */
820 result = dalloc_stradd(dict, "kMDSStorePathScopes");
821 if (result != 0) {
822 return false;
824 array = dalloc_zero(dict, sl_array_t);
825 if (array == NULL) {
826 return false;
828 s = talloc_strdup(dict, "/");
829 if (s == NULL) {
830 return false;
832 talloc_set_name(s, "smb_ucs2_t *");
833 result = dalloc_add(array, s, smb_ucs2_t *);
834 if (result != 0) {
835 return false;
837 result = dalloc_add(dict, array, sl_array_t);
838 if (result != 0) {
839 return false;
842 result = dalloc_add(reply, dict, sl_dict_t);
843 if (result != 0) {
844 return false;
847 return true;
850 static void slq_close_timer(struct tevent_context *ev,
851 struct tevent_timer *te,
852 struct timeval current_time,
853 void *private_data)
855 struct sl_query *slq = talloc_get_type_abort(
856 private_data, struct sl_query);
857 struct mds_ctx *mds_ctx = slq->mds_ctx;
859 SLQ_DEBUG(10, slq, "expired");
861 TALLOC_FREE(slq);
863 if (CHECK_DEBUGLVL(10)) {
864 for (slq = mds_ctx->query_list; slq != NULL; slq = slq->next) {
865 SLQ_DEBUG(10, slq, "pending");
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.
875 static bool mdssvc_real_scope(struct sl_query *slq, const char *fake_scope)
877 size_t sname_len = strlen(slq->mds_ctx->sharename);
878 size_t fake_scope_len = strlen(fake_scope);
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;
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;
893 return true;
897 * Begin a search query
899 static bool slrpc_open_query(struct mds_ctx *mds_ctx,
900 const DALLOC_CTX *query, DALLOC_CTX *reply)
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;
917 array = dalloc_zero(reply, sl_array_t);
918 if (array == NULL) {
919 return false;
922 /* Allocate and initialize query object */
923 slq = talloc_zero(mds_ctx, struct sl_query);
924 if (slq == NULL) {
925 return false;
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;
932 talloc_set_destructor(slq, slq_destructor_cb);
933 slq->state = SLQ_STATE_NEW;
934 slq->mds_ctx = mds_ctx;
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;
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;
955 querystring_len = talloc_array_length(querystring);
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;
962 dest = slq->query_string;
963 dest_remaining = talloc_array_length(dest);
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;
975 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
976 "uint64_t", 1);
977 if (uint64p == NULL) {
978 goto error;
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;
986 slq->ctx2 = *uint64p;
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;
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);
1003 if (scope == NULL) {
1004 DBG_ERR("Failed to parse kMDScopeArray\n");
1005 goto error;
1008 ok = mdssvc_real_scope(slq, scope);
1009 if (!ok) {
1010 goto error;
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;
1022 slq->reqinfo = talloc_steal(slq, reqinfo);
1023 DEBUG(10, ("requested attributes: %s", dalloc_dump(reqinfo, 0)));
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;
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;
1043 SLQ_DEBUG(10, slq, "new");
1045 DLIST_ADD(mds_ctx->query_list, slq);
1047 ok = mds_ctx->backend->search_start(slq);
1048 if (!ok) {
1049 DBG_ERR("backend search_start failed\n");
1050 goto error;
1053 sl_result = 0;
1054 result = dalloc_add_copy(array, &sl_result, uint64_t);
1055 if (result != 0) {
1056 goto error;
1058 result = dalloc_add(reply, array, sl_array_t);
1059 if (result != 0) {
1060 goto error;
1062 return true;
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;
1071 result = dalloc_add(reply, array, sl_array_t);
1072 if (result != 0) {
1073 return false;
1075 return true;
1079 * Fetch results of a query
1081 static bool slrpc_fetch_query_results(struct mds_ctx *mds_ctx,
1082 const DALLOC_CTX *query,
1083 DALLOC_CTX *reply)
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;
1092 array = dalloc_zero(reply, sl_array_t);
1093 if (array == NULL) {
1094 return false;
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;
1103 ctx1 = *uint64p;
1105 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1106 "uint64_t", 2);
1107 if (uint64p == NULL) {
1108 goto error;
1110 ctx2 = *uint64p;
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;
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;
1129 SLQ_DEBUG(10, slq, "fetch");
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;
1141 if (slq->state == SLQ_STATE_FULL) {
1142 slq->state = SLQ_STATE_RUNNING;
1143 slq->mds_ctx->backend->search_cont(slq);
1145 break;
1147 case SLQ_STATE_ERROR:
1148 DEBUG(1, ("query in error state\n"));
1149 goto error;
1151 default:
1152 DEBUG(1, ("unexpected query state %d\n", slq->state));
1153 goto error;
1156 result = dalloc_add(reply, array, sl_array_t);
1157 if (result != 0) {
1158 goto error;
1160 return true;
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;
1169 result = dalloc_add(reply, array, sl_array_t);
1170 if (result != 0) {
1171 return false;
1173 return true;
1177 * Store metadata attributes for a CNID
1179 static bool slrpc_store_attributes(struct mds_ctx *mds_ctx,
1180 const DALLOC_CTX *query, DALLOC_CTX *reply)
1182 uint64_t sl_result;
1183 sl_array_t *array;
1184 int result;
1186 array = dalloc_zero(reply, sl_array_t);
1187 if (array == NULL) {
1188 return false;
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.
1198 sl_result = 0;
1199 result = dalloc_add_copy(array, &sl_result, uint64_t);
1200 if (result != 0) {
1201 return false;
1203 result = dalloc_add(reply, array, sl_array_t);
1204 if (result != 0) {
1205 return false;
1208 return true;
1212 * Fetch supported metadata attributes for a CNID
1214 static bool slrpc_fetch_attributenames(struct mds_ctx *mds_ctx,
1215 const DALLOC_CTX *query,
1216 DALLOC_CTX *reply)
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;
1228 cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 1);
1229 if (cnids == NULL) {
1230 return false;
1233 p = dalloc_get_object(cnids->ca_cnids, 0);
1234 if (p == NULL) {
1235 return NULL;
1237 memcpy(&id, p, sizeof(uint64_t));
1239 /* Result array */
1240 array = dalloc_zero(reply, sl_array_t);
1241 if (array == NULL) {
1242 return false;
1245 result = dalloc_add(reply, array, sl_array_t);
1246 if (result != 0) {
1247 return false;
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;
1257 /* Return CNID array */
1258 replycnids = talloc_zero(reply, sl_cnids_t);
1259 if (replycnids == NULL) {
1260 return false;
1263 replycnids->ca_cnids = dalloc_new(cnids);
1264 if (replycnids->ca_cnids == NULL) {
1265 return false;
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;
1274 result = dalloc_add(array, replycnids, sl_cnids_t);
1275 if (result != 0) {
1276 return false;
1280 * FIXME: this should return the real attributes from all
1281 * known metadata sources (Tracker and filesystem)
1283 mdattrs = dalloc_zero(reply, sl_array_t);
1284 if (mdattrs == NULL) {
1285 return false;
1288 result = dalloc_stradd(mdattrs, "kMDItemFSName");
1289 if (result != 0) {
1290 return false;
1292 result = dalloc_stradd(mdattrs, "kMDItemDisplayName");
1293 if (result != 0) {
1294 return false;
1296 result = dalloc_stradd(mdattrs, "kMDItemFSSize");
1297 if (result != 0) {
1298 return false;
1300 result = dalloc_stradd(mdattrs, "kMDItemFSOwnerUserID");
1301 if (result != 0) {
1302 return false;
1304 result = dalloc_stradd(mdattrs, "kMDItemFSOwnerGroupID");
1305 if (result != 0) {
1306 return false;
1308 result = dalloc_stradd(mdattrs, "kMDItemFSContentChangeDate");
1309 if (result != 0) {
1310 return false;
1313 fmeta = dalloc_zero(reply, sl_filemeta_t);
1314 if (fmeta == NULL) {
1315 return false;
1317 result = dalloc_add(fmeta, mdattrs, sl_array_t);
1318 if (result != 0) {
1319 return false;
1321 result = dalloc_add(array, fmeta, sl_filemeta_t);
1322 if (result != 0) {
1323 return false;
1326 return true;
1330 * Fetch metadata attribute values for a CNID
1332 static bool slrpc_fetch_attributes(struct mds_ctx *mds_ctx,
1333 const DALLOC_CTX *query, DALLOC_CTX *reply)
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;
1354 array = dalloc_zero(reply, sl_array_t);
1355 if (array == NULL) {
1356 return false;
1358 replycnids = talloc_zero(reply, sl_cnids_t);
1359 if (replycnids == NULL) {
1360 goto error;
1362 replycnids->ca_cnids = dalloc_new(replycnids);
1363 if (replycnids->ca_cnids == NULL) {
1364 goto error;
1366 fm = dalloc_zero(array, sl_filemeta_t);
1367 if (fm == NULL) {
1368 goto error;
1370 fm_array = dalloc_zero(fm, sl_array_t);
1371 if (fm_array == NULL) {
1372 goto error;
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;
1380 reqinfo = dalloc_get(query, "DALLOC_CTX", 0, "sl_array_t", 1);
1381 if (reqinfo == NULL) {
1382 goto error;
1385 cnids = dalloc_get(query, "DALLOC_CTX", 0, "sl_cnids_t", 2);
1386 if (cnids == NULL) {
1387 goto error;
1389 p = dalloc_get_object(cnids->ca_cnids, 0);
1390 if (p == NULL) {
1391 goto error;
1393 memcpy(&ino, p, sizeof(uint64_t));
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;
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;
1412 memcpy(&p, val.dptr, sizeof(p));
1413 elem = talloc_get_type_abort(p, struct sl_inode_path_map);
1414 path = elem->path;
1416 sp = &elem->st;
1419 ok = add_filemeta(mds_ctx, reqinfo, fm_array, path, sp);
1420 if (!ok) {
1421 goto error;
1424 sl_result = 0;
1425 result = dalloc_add_copy(array, &sl_result, uint64_t);
1426 if (result != 0) {
1427 goto error;
1429 result = dalloc_add(array, replycnids, sl_cnids_t);
1430 if (result != 0) {
1431 goto error;
1433 result = dalloc_add(fm, fm_array, sl_array_t);
1434 if (result != 0) {
1435 goto error;
1437 result = dalloc_add(array, fm, sl_filemeta_t);
1438 if (result != 0) {
1439 goto error;
1441 result = dalloc_add(reply, array, sl_array_t);
1442 if (result != 0) {
1443 goto error;
1446 TALLOC_FREE(smb_fname);
1447 return true;
1449 error:
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;
1457 result = dalloc_add(reply, array, sl_array_t);
1458 if (result != 0) {
1459 return false;
1462 return true;
1466 * Close a query
1468 static bool slrpc_close_query(struct mds_ctx *mds_ctx,
1469 const DALLOC_CTX *query, DALLOC_CTX *reply)
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;
1477 array = dalloc_zero(reply, sl_array_t);
1478 if (array == NULL) {
1479 return false;
1482 /* Context */
1483 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1484 "uint64_t", 1);
1485 if (uint64p == NULL) {
1486 goto done;
1488 ctx1 = *uint64p;
1490 uint64p = dalloc_get(query, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1491 "uint64_t", 2);
1492 if (uint64p == NULL) {
1493 goto done;
1495 ctx2 = *uint64p;
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;
1505 SLQ_DEBUG(10, slq, "close");
1506 TALLOC_FREE(slq);
1508 done:
1509 sl_res = UINT64_MAX;
1510 result = dalloc_add_copy(array, &sl_res, uint64_t);
1511 if (result != 0) {
1512 return false;
1514 result = dalloc_add(reply, array, sl_array_t);
1515 if (result != 0) {
1516 return false;
1518 return true;
1521 static struct mdssvc_ctx *mdssvc_init(struct tevent_context *ev)
1523 bool ok;
1525 if (mdssvc_ctx != NULL) {
1526 return mdssvc_ctx;
1529 mdssvc_ctx = talloc_zero(ev, struct mdssvc_ctx);
1530 if (mdssvc_ctx == NULL) {
1531 return NULL;
1534 mdssvc_ctx->ev_ctx = ev;
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;
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;
1550 #endif
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;
1559 #endif
1561 return mdssvc_ctx;
1565 * Init callbacks at startup
1567 * This gets typically called in the main parent smbd which means we can't
1568 * initialize our global state here.
1570 bool mds_init(struct messaging_context *msg_ctx)
1572 return true;
1575 bool mds_shutdown(void)
1577 bool ok;
1579 if (mdssvc_ctx == NULL) {
1580 return false;
1583 ok = mdsscv_backend_noindex.shutdown(mdssvc_ctx);
1584 if (!ok) {
1585 goto fail;
1588 #ifdef HAVE_SPOTLIGHT_BACKEND_ES
1589 ok = mdsscv_backend_es.shutdown(mdssvc_ctx);
1590 if (!ok) {
1591 goto fail;
1593 #endif
1595 #ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
1596 ok = mdsscv_backend_tracker.shutdown(mdssvc_ctx);
1597 if (!ok) {
1598 goto fail;
1600 #endif
1602 ok = true;
1603 fail:
1604 TALLOC_FREE(mdssvc_ctx);
1605 return ok;
1609 * Tear down connections and free all resources
1611 static int mds_ctx_destructor_cb(struct mds_ctx *mds_ctx)
1614 * We need to free query_list before ino_path_map
1616 while (mds_ctx->query_list != NULL) {
1618 * slq destructor removes element from list.
1619 * Don't use TALLOC_FREE()!
1621 talloc_free(mds_ctx->query_list);
1623 TALLOC_FREE(mds_ctx->ino_path_map);
1625 if (mds_ctx->conn != NULL) {
1626 SMB_VFS_DISCONNECT(mds_ctx->conn);
1627 conn_free(mds_ctx->conn);
1630 ZERO_STRUCTP(mds_ctx);
1632 return 0;
1636 * Initialise a context per RPC bind
1638 * This ends up being called for every tcon, because the client does a
1639 * RPC bind for every tcon, so this is actually a per tcon context.
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)
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;
1660 if (!lp_spotlight(snum)) {
1661 return NT_STATUS_WRONG_VOLUME;
1664 mds_ctx = talloc_zero(mem_ctx, struct mds_ctx);
1665 if (mds_ctx == NULL) {
1666 return NT_STATUS_NO_MEMORY;
1668 talloc_set_destructor(mds_ctx, mds_ctx_destructor_cb);
1670 mds_ctx->mdssvc_ctx = mdssvc_init(ev);
1671 if (mds_ctx->mdssvc_ctx == NULL) {
1672 return NT_STATUS_NO_MEMORY;
1675 backend = lp_spotlight_backend(snum);
1676 switch (backend) {
1677 case SPOTLIGHT_BACKEND_NOINDEX:
1678 mds_ctx->backend = &mdsscv_backend_noindex;
1679 break;
1681 #ifdef HAVE_SPOTLIGHT_BACKEND_ES
1682 case SPOTLIGHT_BACKEND_ES:
1683 mds_ctx->backend = &mdsscv_backend_es;
1684 break;
1685 #endif
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;
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;
1707 mds_ctx->ic_nfc_to_nfd = iconv_hnd;
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;
1717 mds_ctx->ic_nfd_to_nfc = iconv_hnd;
1719 mds_ctx->sharename = talloc_strdup(mds_ctx, sharename);
1720 if (mds_ctx->sharename == NULL) {
1721 status = NT_STATUS_NO_MEMORY;
1722 goto error;
1725 mds_ctx->spath = talloc_strdup(mds_ctx, path);
1726 if (mds_ctx->spath == NULL) {
1727 status = NT_STATUS_NO_MEMORY;
1728 goto error;
1730 mds_ctx->spath_len = strlen(path);
1732 mds_ctx->snum = snum;
1733 mds_ctx->pipe_session_info = session_info;
1735 if (session_info->security_token->num_sids < 1) {
1736 status = NT_STATUS_BAD_LOGON_SESSION_STATE;
1737 goto error;
1739 sid_copy(&mds_ctx->sid, &session_info->security_token->sids[0]);
1740 mds_ctx->uid = session_info->unix_token->uid;
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;
1749 status = create_conn_struct_cwd(mds_ctx,
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;
1762 conn_basedir = (struct smb_filename) {
1763 .base_name = mds_ctx->conn->connectpath,
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;
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;
1781 *_mds_ctx = mds_ctx;
1782 return NT_STATUS_OK;
1784 error:
1785 if (mds_ctx->ic_nfc_to_nfd != NULL) {
1786 smb_iconv_close(mds_ctx->ic_nfc_to_nfd);
1788 if (mds_ctx->ic_nfd_to_nfc != NULL) {
1789 smb_iconv_close(mds_ctx->ic_nfd_to_nfc);
1792 TALLOC_FREE(mds_ctx);
1793 return status;
1797 * Dispatch a Spotlight RPC command
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)
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,
1813 NTSTATUS status;
1815 if (CHECK_DEBUGLVL(10)) {
1816 const struct sl_query *slq;
1818 for (slq = mds_ctx->query_list; slq != NULL; slq = slq->next) {
1819 SLQ_DEBUG(10, slq, "pending");
1823 response_blob->length = 0;
1825 DEBUG(10, ("share path: %s\n", mds_ctx->spath));
1827 query = dalloc_new(mds_ctx);
1828 if (query == NULL) {
1829 ok = false;
1830 goto cleanup;
1832 reply = dalloc_new(mds_ctx);
1833 if (reply == NULL) {
1834 ok = false;
1835 goto cleanup;
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;
1845 DEBUG(5, ("%s", dalloc_dump(query, 0)));
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;
1855 DEBUG(10, ("Spotlight RPC cmd: %s\n", rpccmd));
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;
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;
1873 ok = slcmd->function(mds_ctx, query, reply);
1874 if (!ok) {
1875 goto cleanup;
1878 DBG_DEBUG("%s", dalloc_dump(reply, 0));
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;
1889 cleanup:
1890 talloc_free(query);
1891 talloc_free(reply);
1892 return ok;