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/>.
22 #include "librpc/gen_ndr/auth.h"
23 #include "dbwrap/dbwrap.h"
24 #include "lib/util/dlinklist.h"
25 #include "lib/util/util_tdb.h"
26 #include "lib/util/time_basic.h"
27 #include "lib/dbwrap/dbwrap_rbt.h"
28 #include "libcli/security/dom_sid.h"
30 #include "mdssvc_noindex.h"
31 #ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
32 #include "mdssvc_tracker.h"
34 #ifdef HAVE_SPOTLIGHT_BACKEND_ES
35 #include "mdssvc_es.h"
39 #define DBGC_CLASS DBGC_RPC_SRV
43 bool (*function
)(struct mds_ctx
*mds_ctx
,
44 const DALLOC_CTX
*query
,
48 struct slq_destroy_state
{
49 struct tevent_context
*ev
;
54 * This is a static global because we may be called multiple times and
55 * we only want one mdssvc_ctx per connection to Tracker.
57 * The client will bind multiple times to the mdssvc RPC service, once
58 * for every tree connect.
60 static struct mdssvc_ctx
*mdssvc_ctx
= NULL
;
63 * If these functions return an error, they hit something like a non
64 * recoverable talloc error. Most errors are dealt with by returning
65 * an error code in the Spotlight RPC reply.
67 static bool slrpc_fetch_properties(struct mds_ctx
*mds_ctx
,
68 const DALLOC_CTX
*query
, DALLOC_CTX
*reply
);
69 static bool slrpc_open_query(struct mds_ctx
*mds_ctx
,
70 const DALLOC_CTX
*query
, DALLOC_CTX
*reply
);
71 static bool slrpc_fetch_query_results(struct mds_ctx
*mds_ctx
,
72 const DALLOC_CTX
*query
, DALLOC_CTX
*reply
);
73 static bool slrpc_store_attributes(struct mds_ctx
*mds_ctx
,
74 const DALLOC_CTX
*query
, DALLOC_CTX
*reply
);
75 static bool slrpc_fetch_attributenames(struct mds_ctx
*mds_ctx
,
76 const DALLOC_CTX
*query
, DALLOC_CTX
*reply
);
77 static bool slrpc_fetch_attributes(struct mds_ctx
*mds_ctx
,
78 const DALLOC_CTX
*query
, DALLOC_CTX
*reply
);
79 static bool slrpc_close_query(struct mds_ctx
*mds_ctx
,
80 const DALLOC_CTX
*query
, DALLOC_CTX
*reply
);
82 /************************************************
83 * Misc utility functions
84 ************************************************/
87 * Add requested metadata for a query result element
89 * This could be rewritten to something more sophisticated like
90 * querying metadata from Tracker.
92 * If path or sp is NULL, simply add nil values for all attributes.
94 static bool add_filemeta(struct mds_ctx
*mds_ctx
,
98 const struct stat_ex
*sp
)
102 int i
, metacount
, result
;
106 const char *attribute
;
108 const char *nfc_path
= path
;
110 char *nfd_path
= NULL
;
112 size_t dest_remaining
;
115 metacount
= dalloc_size(reqinfo
);
116 if (metacount
== 0 || path
== NULL
|| sp
== NULL
) {
117 result
= dalloc_add_copy(fm_array
, &nil
, sl_nil_t
);
124 meta
= dalloc_zero(fm_array
, sl_array_t
);
129 nfc_len
= strlen(nfc_path
);
131 * Simple heuristic, strlen by two should give enough room for NFC to
134 nfd_buf_size
= nfc_len
* 2;
135 nfd_path
= talloc_array(meta
, char, nfd_buf_size
);
136 if (nfd_path
== NULL
) {
140 dest_remaining
= talloc_array_length(dest
);
142 nconv
= smb_iconv(mds_ctx
->ic_nfc_to_nfd
,
147 if (nconv
== (size_t)-1) {
151 for (i
= 0; i
< metacount
; i
++) {
152 attribute
= dalloc_get_object(reqinfo
, i
);
153 if (attribute
== NULL
) {
156 if (strcmp(attribute
, "kMDItemDisplayName") == 0
157 || strcmp(attribute
, "kMDItemFSName") == 0) {
158 p
= strrchr(nfd_path
, '/');
160 result
= dalloc_stradd(meta
, p
+ 1);
165 } else if (strcmp(attribute
, "kMDItemPath") == 0) {
166 result
= dalloc_stradd(meta
, nfd_path
);
170 } else if (strcmp(attribute
, "kMDItemFSSize") == 0) {
171 uint64var
= sp
->st_ex_size
;
172 result
= dalloc_add_copy(meta
, &uint64var
, uint64_t);
176 } else if (strcmp(attribute
, "kMDItemFSOwnerUserID") == 0) {
177 uint64var
= sp
->st_ex_uid
;
178 result
= dalloc_add_copy(meta
, &uint64var
, uint64_t);
182 } else if (strcmp(attribute
, "kMDItemFSOwnerGroupID") == 0) {
183 uint64var
= sp
->st_ex_gid
;
184 result
= dalloc_add_copy(meta
, &uint64var
, uint64_t);
188 } else if (strcmp(attribute
, "kMDItemFSContentChangeDate") == 0) {
189 sl_time
.tv_sec
= sp
->st_ex_mtime
.tv_sec
;
190 result
= dalloc_add_copy(meta
, &sl_time
, sl_time_t
);
195 result
= dalloc_add_copy(meta
, &nil
, sl_nil_t
);
202 result
= dalloc_add(fm_array
, meta
, sl_array_t
);
209 static int cnid_comp_fn(const void *p1
, const void *p2
)
211 const uint64_t *cnid1
= p1
, *cnid2
= p2
;
212 if (*cnid1
== *cnid2
) {
215 if (*cnid1
< *cnid2
) {
222 * Create a sorted copy of a CNID array
224 static bool sort_cnids(struct sl_query
*slq
, const DALLOC_CTX
*d
)
226 uint64_t *cnids
= NULL
;
230 cnids
= talloc_array(slq
, uint64_t, dalloc_size(d
));
235 for (i
= 0; i
< dalloc_size(d
); i
++) {
236 p
= dalloc_get_object(d
, i
);
240 memcpy(&cnids
[i
], p
, sizeof(uint64_t));
242 qsort(cnids
, dalloc_size(d
), sizeof(uint64_t), cnid_comp_fn
);
245 slq
->cnids_num
= dalloc_size(d
);
251 * Allocate result handle used in the async Tracker cursor result
252 * handler for storing results
254 static bool create_result_handle(struct sl_query
*slq
)
257 struct sl_rslts
*query_results
;
260 if (slq
->query_results
) {
261 DEBUG(1, ("unexpected existing result handle\n"));
265 query_results
= talloc_zero(slq
, struct sl_rslts
);
266 if (query_results
== NULL
) {
271 query_results
->cnids
= talloc_zero(query_results
, sl_cnids_t
);
272 if (query_results
->cnids
== NULL
) {
275 query_results
->cnids
->ca_cnids
= dalloc_new(query_results
->cnids
);
276 if (query_results
->cnids
->ca_cnids
== NULL
) {
280 query_results
->cnids
->ca_unkn1
= 0xadd;
281 if (slq
->ctx2
> UINT32_MAX
) {
282 DEBUG(1,("64bit ctx2 id too large: 0x%jx", (uintmax_t)slq
->ctx2
));
285 query_results
->cnids
->ca_context
= (uint32_t)slq
->ctx2
;
288 query_results
->fm_array
= dalloc_zero(query_results
, sl_array_t
);
289 if (query_results
->fm_array
== NULL
) {
293 /* For some reason the list of results always starts with a nil entry */
294 result
= dalloc_add_copy(query_results
->fm_array
, &nil
, sl_nil_t
);
299 slq
->query_results
= query_results
;
303 static bool add_results(sl_array_t
*array
, struct sl_query
*slq
)
311 fm
= dalloc_zero(array
, sl_filemeta_t
);
316 result
= dalloc_add_copy(array
, &status
, uint64_t);
320 result
= dalloc_add(array
, slq
->query_results
->cnids
, sl_cnids_t
);
324 if (slq
->query_results
->num_results
> 0) {
325 result
= dalloc_add(fm
, slq
->query_results
->fm_array
, sl_array_t
);
330 result
= dalloc_add(array
, fm
, sl_filemeta_t
);
335 /* This ensure the results get clean up after been sent to the client */
336 talloc_move(array
, &slq
->query_results
);
338 ok
= create_result_handle(slq
);
340 DEBUG(1, ("couldn't add result handle\n"));
341 slq
->state
= SLQ_STATE_ERROR
;
348 static const struct slrpc_cmd
*slrpc_cmd_by_name(const char *rpccmd
)
351 static const struct slrpc_cmd cmds
[] = {
352 { "fetchPropertiesForContext:", slrpc_fetch_properties
},
353 { "openQueryWithParams:forContext:", slrpc_open_query
},
354 { "fetchQueryResultsForContext:", slrpc_fetch_query_results
},
355 { "storeAttributes:forOIDArray:context:", slrpc_store_attributes
},
356 { "fetchAttributeNamesForOIDArray:context:", slrpc_fetch_attributenames
},
357 { "fetchAttributes:forOIDArray:context:", slrpc_fetch_attributes
},
358 { "fetchAllAttributes:forOIDArray:context:", slrpc_fetch_attributes
},
359 { "closeQueryForContext:", slrpc_close_query
},
362 for (i
= 0; i
< ARRAY_SIZE(cmds
); i
++) {
365 cmp
= strcmp(cmds
[i
].name
, rpccmd
);
375 * Search the list of active queries given their context ids
377 static struct sl_query
*slq_for_ctx(struct mds_ctx
*mds_ctx
,
378 uint64_t ctx1
, uint64_t ctx2
)
382 for (q
= mds_ctx
->query_list
; q
; q
= q
->next
) {
383 if ((q
->ctx1
== ctx1
) && (q
->ctx2
== ctx2
)) {
391 static int slq_destructor_cb(struct sl_query
*slq
)
393 SLQ_DEBUG(10, slq
, "destroying");
395 /* Free all entries before freeing the slq handle! */
396 TALLOC_FREE(slq
->entries_ctx
);
397 TALLOC_FREE(slq
->te
);
399 if (slq
->mds_ctx
!= NULL
) {
400 DLIST_REMOVE(slq
->mds_ctx
->query_list
, slq
);
404 TALLOC_FREE(slq
->backend_private
);
410 * Remove talloc_refcounted entry from mapping db
412 * Multiple queries (via the slq handle) may reference a
413 * sl_inode_path_map entry, when the last reference goes away as the
414 * queries are closed and this gets called to remove the entry from
417 static int ino_path_map_destr_cb(struct sl_inode_path_map
*entry
)
422 key
= make_tdb_data((uint8_t *)&entry
->ino
, sizeof(entry
->ino
));
424 status
= dbwrap_delete(entry
->mds_ctx
->ino_path_map
, key
);
425 if (!NT_STATUS_IS_OK(status
)) {
426 DEBUG(1, ("Failed to delete record: %s\n", nt_errstr(status
)));
430 DBG_DEBUG("deleted [0x%"PRIx64
"] [%s]\n", entry
->ino
, entry
->path
);
435 * Add result to inode->path mapping dbwrap rbt db
437 * This is necessary as a CNID db substitute, ie we need a way to
438 * simulate unique, constant numerical identifiers for paths with an
439 * API that supports mapping from id to path.
441 * Entries are talloc'ed of the query, using talloc_reference() if
442 * multiple queries returned the same result. That way we can cleanup
443 * entries by calling talloc_free() on the query slq handles.
446 static bool inode_map_add(struct sl_query
*slq
, uint64_t ino
, const char *path
)
449 struct sl_inode_path_map
*entry
;
453 key
= make_tdb_data((uint8_t *)&ino
, sizeof(ino
));
454 status
= dbwrap_fetch(slq
->mds_ctx
->ino_path_map
, slq
, key
, &value
);
456 if (NT_STATUS_IS_OK(status
)) {
458 * We have one db, so when different parallel queries
459 * return the same file, we have to refcount entries
463 if (value
.dsize
!= sizeof(void *)) {
464 DEBUG(1, ("invalide dsize\n"));
467 memcpy(&p
, value
.dptr
, sizeof(p
));
468 entry
= talloc_get_type_abort(p
, struct sl_inode_path_map
);
470 DEBUG(10, ("map: %s\n", entry
->path
));
472 entry
= talloc_reference(slq
->entries_ctx
, entry
);
474 DEBUG(1, ("talloc_reference failed\n"));
480 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
481 DEBUG(1, ("dbwrap_fetch failed %s\n", nt_errstr(status
)));
485 entry
= talloc_zero(slq
->entries_ctx
, struct sl_inode_path_map
);
487 DEBUG(1, ("talloc failed\n"));
492 entry
->mds_ctx
= slq
->mds_ctx
;
493 entry
->path
= talloc_strdup(entry
, path
);
494 if (entry
->path
== NULL
) {
495 DEBUG(1, ("talloc failed\n"));
500 status
= dbwrap_store(slq
->mds_ctx
->ino_path_map
, key
,
501 make_tdb_data((void *)&entry
, sizeof(void *)), 0);
502 if (!NT_STATUS_IS_OK(status
)) {
503 DEBUG(1, ("Failed to store record: %s\n", nt_errstr(status
)));
508 talloc_set_destructor(entry
, ino_path_map_destr_cb
);
513 bool mds_add_result(struct sl_query
*slq
, const char *path
)
521 * We're in a tevent callback which means in the case of
522 * running as external RPC service we're running as root and
525 if (!become_authenticated_pipe_user(slq
->mds_ctx
->pipe_session_info
)) {
526 DBG_ERR("can't become authenticated user: %d\n",
528 smb_panic("can't become authenticated user");
531 if (geteuid() != slq
->mds_ctx
->uid
) {
532 DBG_ERR("uid mismatch: %d/%d\n", geteuid(), slq
->mds_ctx
->uid
);
533 smb_panic("uid mismatch");
537 * We've changed identity to the authenticated pipe user, so
538 * any function exit below must ensure we switch back
541 result
= sys_stat(path
, &sb
, false);
543 unbecome_authenticated_pipe_user();
546 result
= access(path
, R_OK
);
548 unbecome_authenticated_pipe_user();
552 unbecome_authenticated_pipe_user();
554 ino64
= sb
.st_ex_ino
;
557 * Check whether the found element is in the requested
558 * set of IDs. Note that we're faking CNIDs by using
559 * filesystem inode numbers here
572 * Add inode number and filemeta to result set, this is what
573 * we return as part of the result set of a query
575 result
= dalloc_add_copy(slq
->query_results
->cnids
->ca_cnids
,
579 DBG_ERR("dalloc error\n");
580 slq
->state
= SLQ_STATE_ERROR
;
583 ok
= add_filemeta(slq
->mds_ctx
,
585 slq
->query_results
->fm_array
,
589 DBG_ERR("add_filemeta error\n");
590 slq
->state
= SLQ_STATE_ERROR
;
594 ok
= inode_map_add(slq
, ino64
, path
);
596 DEBUG(1, ("inode_map_add error\n"));
597 slq
->state
= SLQ_STATE_ERROR
;
601 slq
->query_results
->num_results
++;
605 /***********************************************************
606 * Spotlight RPC functions
607 ***********************************************************/
609 static bool slrpc_fetch_properties(struct mds_ctx
*mds_ctx
,
610 const DALLOC_CTX
*query
, DALLOC_CTX
*reply
)
620 dict
= dalloc_zero(reply
, sl_dict_t
);
625 /* kMDSStoreHasPersistentUUID = false */
626 result
= dalloc_stradd(dict
, "kMDSStoreHasPersistentUUID");
631 result
= dalloc_add_copy(dict
, &b
, sl_bool_t
);
636 /* kMDSStoreIsBackup = false */
637 result
= dalloc_stradd(dict
, "kMDSStoreIsBackup");
642 result
= dalloc_add_copy(dict
, &b
, sl_bool_t
);
647 /* kMDSStoreUUID = uuid */
648 result
= dalloc_stradd(dict
, "kMDSStoreUUID");
652 memcpy(uuid
.sl_uuid
, "fakeuuidfakeuuid", sizeof(uuid
.sl_uuid
));
653 result
= dalloc_add_copy(dict
, &uuid
, sl_uuid_t
);
658 /* kMDSStoreSupportsVolFS = true */
659 result
= dalloc_stradd(dict
, "kMDSStoreSupportsVolFS");
664 result
= dalloc_add_copy(dict
, &b
, sl_bool_t
);
669 /* kMDSVolumeUUID = uuid */
670 result
= dalloc_stradd(dict
, "kMDSVolumeUUID");
674 memcpy(uuid
.sl_uuid
, "fakeuuidfakeuuid", sizeof(uuid
.sl_uuid
));
675 result
= dalloc_add_copy(dict
, &uuid
, sl_uuid_t
);
680 /* kMDSDiskStoreSpindleNumber = 1 (fake) */
681 result
= dalloc_stradd(dict
, "kMDSDiskStoreSpindleNumber");
686 result
= dalloc_add_copy(dict
, &u
, uint64_t);
691 /* kMDSDiskStorePolicy = 3 (whatever that means, taken from OS X) */
692 result
= dalloc_stradd(dict
, "kMDSDiskStorePolicy");
697 result
= dalloc_add_copy(dict
, &u
, uint64_t);
702 /* kMDSStoreMetaScopes array */
703 array
= dalloc_zero(dict
, sl_array_t
);
707 result
= dalloc_stradd(array
, "kMDQueryScopeComputer");
711 result
= dalloc_stradd(array
, "kMDQueryScopeAllIndexed");
715 result
= dalloc_stradd(array
, "kMDQueryScopeComputerIndexed");
719 result
= dalloc_add(dict
, array
, sl_array_t
);
724 /* kMDSStoreDevice = 0x1000003 (whatever that means, taken from OS X) */
725 result
= dalloc_stradd(dict
, "kMDSStoreDevice");
730 result
= dalloc_add_copy(dict
, &u
, uint64_t);
735 /* kMDSStoreSupportsTCC = true (whatever that means, taken from OS X) */
736 result
= dalloc_stradd(dict
, "kMDSStoreSupportsTCC");
741 result
= dalloc_add_copy(dict
, &b
, sl_bool_t
);
746 /* kMDSStorePathScopes = ["/"] (whatever that means, taken from OS X) */
747 result
= dalloc_stradd(dict
, "kMDSStorePathScopes");
751 array
= dalloc_zero(dict
, sl_array_t
);
755 s
= talloc_strdup(dict
, "/");
759 talloc_set_name(s
, "smb_ucs2_t *");
760 result
= dalloc_add(array
, s
, smb_ucs2_t
*);
764 result
= dalloc_add(dict
, array
, sl_array_t
);
769 result
= dalloc_add(reply
, dict
, sl_dict_t
);
777 static void slq_close_timer(struct tevent_context
*ev
,
778 struct tevent_timer
*te
,
779 struct timeval current_time
,
782 struct sl_query
*slq
= talloc_get_type_abort(
783 private_data
, struct sl_query
);
784 struct mds_ctx
*mds_ctx
= slq
->mds_ctx
;
786 SLQ_DEBUG(10, slq
, "expired");
790 if (CHECK_DEBUGLVL(10)) {
791 for (slq
= mds_ctx
->query_list
; slq
!= NULL
; slq
= slq
->next
) {
792 SLQ_DEBUG(10, slq
, "pending");
798 * Begin a search query
800 static bool slrpc_open_query(struct mds_ctx
*mds_ctx
,
801 const DALLOC_CTX
*query
, DALLOC_CTX
*reply
)
807 sl_array_t
*array
, *path_scope
;
809 struct sl_query
*slq
= NULL
;
811 const char *querystring
= NULL
;
812 size_t querystring_len
;
814 size_t dest_remaining
;
818 array
= dalloc_zero(reply
, sl_array_t
);
823 /* Allocate and initialize query object */
824 slq
= talloc_zero(mds_ctx
, struct sl_query
);
828 slq
->entries_ctx
= talloc_named_const(slq
, 0, "struct sl_query.entries_ctx");
829 if (slq
->entries_ctx
== NULL
) {
833 talloc_set_destructor(slq
, slq_destructor_cb
);
834 slq
->state
= SLQ_STATE_NEW
;
835 slq
->mds_ctx
= mds_ctx
;
837 slq
->last_used
= timeval_current();
838 slq
->start_time
= slq
->last_used
;
839 slq
->expire_time
= timeval_add(&slq
->last_used
, MAX_SL_RUNTIME
, 0);
840 slq
->te
= tevent_add_timer(global_event_context(), slq
,
841 slq
->expire_time
, slq_close_timer
, slq
);
842 if (slq
->te
== NULL
) {
843 DEBUG(1, ("tevent_add_timer failed\n"));
847 querystring
= dalloc_value_for_key(query
, "DALLOC_CTX", 0,
850 if (querystring
== NULL
) {
851 DEBUG(1, ("missing kMDQueryString\n"));
855 querystring_len
= talloc_array_length(querystring
);
857 slq
->query_string
= talloc_array(slq
, char, querystring_len
);
858 if (slq
->query_string
== NULL
) {
859 DEBUG(1, ("out of memory\n"));
862 dest
= slq
->query_string
;
863 dest_remaining
= talloc_array_length(dest
);
865 nconv
= smb_iconv(mds_ctx
->ic_nfd_to_nfc
,
870 if (nconv
== (size_t)-1) {
871 DBG_ERR("smb_iconv failed for: %s\n", querystring
);
875 uint64p
= dalloc_get(query
, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
877 if (uint64p
== NULL
) {
880 slq
->ctx1
= *uint64p
;
881 uint64p
= dalloc_get(query
, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
883 if (uint64p
== NULL
) {
886 slq
->ctx2
= *uint64p
;
888 path_scope
= dalloc_value_for_key(query
, "DALLOC_CTX", 0,
889 "DALLOC_CTX", 1, "kMDScopeArray");
890 if (path_scope
== NULL
) {
894 scope
= dalloc_get(path_scope
, "char *", 0);
899 slq
->path_scope
= talloc_strdup(slq
, scope
);
900 if (slq
->path_scope
== NULL
) {
904 reqinfo
= dalloc_value_for_key(query
, "DALLOC_CTX", 0,
905 "DALLOC_CTX", 1, "kMDAttributeArray");
906 if (reqinfo
== NULL
) {
910 slq
->reqinfo
= talloc_steal(slq
, reqinfo
);
911 DEBUG(10, ("requested attributes: %s", dalloc_dump(reqinfo
, 0)));
913 cnids
= dalloc_value_for_key(query
, "DALLOC_CTX", 0,
914 "DALLOC_CTX", 1, "kMDQueryItemArray");
916 ok
= sort_cnids(slq
, cnids
->ca_cnids
);
922 ok
= create_result_handle(slq
);
924 DEBUG(1, ("create_result_handle error\n"));
925 slq
->state
= SLQ_STATE_ERROR
;
929 SLQ_DEBUG(10, slq
, "new");
931 DLIST_ADD(mds_ctx
->query_list
, slq
);
933 ok
= mds_ctx
->backend
->search_start(slq
);
935 DBG_ERR("backend search_start failed\n");
940 result
= dalloc_add_copy(array
, &sl_result
, uint64_t);
944 result
= dalloc_add(reply
, array
, sl_array_t
);
951 sl_result
= UINT64_MAX
;
953 result
= dalloc_add_copy(array
, &sl_result
, uint64_t);
957 result
= dalloc_add(reply
, array
, sl_array_t
);
965 * Fetch results of a query
967 static bool slrpc_fetch_query_results(struct mds_ctx
*mds_ctx
,
968 const DALLOC_CTX
*query
,
972 struct sl_query
*slq
= NULL
;
973 uint64_t *uint64p
, ctx1
, ctx2
;
978 array
= dalloc_zero(reply
, sl_array_t
);
983 /* Get query for context */
984 uint64p
= dalloc_get(query
, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
986 if (uint64p
== NULL
) {
991 uint64p
= dalloc_get(query
, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
993 if (uint64p
== NULL
) {
998 slq
= slq_for_ctx(mds_ctx
, ctx1
, ctx2
);
1000 DEBUG(1, ("bad context: [0x%jx,0x%jx]\n",
1001 (uintmax_t)ctx1
, (uintmax_t)ctx2
));
1005 TALLOC_FREE(slq
->te
);
1006 slq
->last_used
= timeval_current();
1007 slq
->expire_time
= timeval_add(&slq
->last_used
, MAX_SL_RUNTIME
, 0);
1008 slq
->te
= tevent_add_timer(global_event_context(), slq
,
1009 slq
->expire_time
, slq_close_timer
, slq
);
1010 if (slq
->te
== NULL
) {
1011 DEBUG(1, ("tevent_add_timer failed\n"));
1015 SLQ_DEBUG(10, slq
, "fetch");
1017 switch (slq
->state
) {
1018 case SLQ_STATE_RUNNING
:
1019 case SLQ_STATE_RESULTS
:
1020 case SLQ_STATE_FULL
:
1021 case SLQ_STATE_DONE
:
1022 ok
= add_results(array
, slq
);
1024 DEBUG(1, ("error adding results\n"));
1027 if (slq
->state
== SLQ_STATE_FULL
) {
1028 slq
->state
= SLQ_STATE_RESULTS
;
1029 slq
->mds_ctx
->backend
->search_cont(slq
);
1033 case SLQ_STATE_ERROR
:
1034 DEBUG(1, ("query in error state\n"));
1038 DEBUG(1, ("unexpected query state %d\n", slq
->state
));
1042 result
= dalloc_add(reply
, array
, sl_array_t
);
1049 status
= UINT64_MAX
;
1051 result
= dalloc_add_copy(array
, &status
, uint64_t);
1055 result
= dalloc_add(reply
, array
, sl_array_t
);
1063 * Store metadata attributes for a CNID
1065 static bool slrpc_store_attributes(struct mds_ctx
*mds_ctx
,
1066 const DALLOC_CTX
*query
, DALLOC_CTX
*reply
)
1072 array
= dalloc_zero(reply
, sl_array_t
);
1073 if (array
== NULL
) {
1078 * FIXME: not implemented. Used by the client for eg setting
1079 * the modification date of the shared directory which clients
1080 * poll indicating changes on the share and cause the client
1085 result
= dalloc_add_copy(array
, &sl_result
, uint64_t);
1089 result
= dalloc_add(reply
, array
, sl_array_t
);
1098 * Fetch supported metadata attributes for a CNID
1100 static bool slrpc_fetch_attributenames(struct mds_ctx
*mds_ctx
,
1101 const DALLOC_CTX
*query
,
1108 sl_cnids_t
*replycnids
;
1109 sl_array_t
*mdattrs
;
1110 sl_filemeta_t
*fmeta
;
1114 cnids
= dalloc_get(query
, "DALLOC_CTX", 0, "sl_cnids_t", 1);
1115 if (cnids
== NULL
) {
1119 p
= dalloc_get_object(cnids
->ca_cnids
, 0);
1123 memcpy(&id
, p
, sizeof(uint64_t));
1126 array
= dalloc_zero(reply
, sl_array_t
);
1127 if (array
== NULL
) {
1131 result
= dalloc_add(reply
, array
, sl_array_t
);
1136 /* Return result value 0 */
1138 result
= dalloc_add_copy(array
, &sl_result
, uint64_t);
1143 /* Return CNID array */
1144 replycnids
= talloc_zero(reply
, sl_cnids_t
);
1145 if (replycnids
== NULL
) {
1149 replycnids
->ca_cnids
= dalloc_new(cnids
);
1150 if (replycnids
->ca_cnids
== NULL
) {
1154 replycnids
->ca_unkn1
= 0xfec;
1155 replycnids
->ca_context
= cnids
->ca_context
;
1156 result
= dalloc_add_copy(replycnids
->ca_cnids
, &id
, uint64_t);
1160 result
= dalloc_add(array
, replycnids
, sl_cnids_t
);
1166 * FIXME: this should return the real attributes from all
1167 * known metadata sources (Tracker and filesystem)
1169 mdattrs
= dalloc_zero(reply
, sl_array_t
);
1170 if (mdattrs
== NULL
) {
1174 result
= dalloc_stradd(mdattrs
, "kMDItemFSName");
1178 result
= dalloc_stradd(mdattrs
, "kMDItemDisplayName");
1182 result
= dalloc_stradd(mdattrs
, "kMDItemFSSize");
1186 result
= dalloc_stradd(mdattrs
, "kMDItemFSOwnerUserID");
1190 result
= dalloc_stradd(mdattrs
, "kMDItemFSOwnerGroupID");
1194 result
= dalloc_stradd(mdattrs
, "kMDItemFSContentChangeDate");
1199 fmeta
= dalloc_zero(reply
, sl_filemeta_t
);
1200 if (fmeta
== NULL
) {
1203 result
= dalloc_add(fmeta
, mdattrs
, sl_array_t
);
1207 result
= dalloc_add(array
, fmeta
, sl_filemeta_t
);
1216 * Fetch metadata attribute values for a CNID
1218 static bool slrpc_fetch_attributes(struct mds_ctx
*mds_ctx
,
1219 const DALLOC_CTX
*query
, DALLOC_CTX
*reply
)
1225 sl_cnids_t
*replycnids
;
1226 sl_array_t
*reqinfo
;
1230 sl_array_t
*fm_array
;
1234 struct sl_inode_path_map
*elem
= NULL
;
1236 TDB_DATA val
= tdb_null
;
1239 array
= dalloc_zero(reply
, sl_array_t
);
1240 if (array
== NULL
) {
1243 replycnids
= talloc_zero(reply
, sl_cnids_t
);
1244 if (replycnids
== NULL
) {
1247 replycnids
->ca_cnids
= dalloc_new(replycnids
);
1248 if (replycnids
->ca_cnids
== NULL
) {
1251 fm
= dalloc_zero(array
, sl_filemeta_t
);
1255 fm_array
= dalloc_zero(fm
, sl_array_t
);
1256 if (fm_array
== NULL
) {
1259 /* For some reason the list of results always starts with a nil entry */
1260 result
= dalloc_add_copy(fm_array
, &nil
, sl_nil_t
);
1265 reqinfo
= dalloc_get(query
, "DALLOC_CTX", 0, "sl_array_t", 1);
1266 if (reqinfo
== NULL
) {
1270 cnids
= dalloc_get(query
, "DALLOC_CTX", 0, "sl_cnids_t", 2);
1271 if (cnids
== NULL
) {
1274 p
= dalloc_get_object(cnids
->ca_cnids
, 0);
1278 memcpy(&ino
, p
, sizeof(uint64_t));
1280 replycnids
->ca_unkn1
= 0xfec;
1281 replycnids
->ca_context
= cnids
->ca_context
;
1282 result
= dalloc_add_copy(replycnids
->ca_cnids
, &ino
, uint64_t);
1287 status
= dbwrap_fetch(mds_ctx
->ino_path_map
, reply
,
1288 make_tdb_data((void*)&ino
, sizeof(uint64_t)),
1290 if (NT_STATUS_IS_OK(status
)) {
1291 if (val
.dsize
!= sizeof(p
)) {
1292 DBG_ERR("invalid record pointer size: %zd\n", val
.dsize
);
1293 TALLOC_FREE(val
.dptr
);
1297 memcpy(&p
, val
.dptr
, sizeof(p
));
1298 elem
= talloc_get_type_abort(p
, struct sl_inode_path_map
);
1301 result
= sys_stat(path
, &sb
, false);
1307 ok
= add_filemeta(mds_ctx
, reqinfo
, fm_array
, path
, &sb
);
1313 result
= dalloc_add_copy(array
, &sl_result
, uint64_t);
1317 result
= dalloc_add(array
, replycnids
, sl_cnids_t
);
1321 result
= dalloc_add(fm
, fm_array
, sl_array_t
);
1325 result
= dalloc_add(array
, fm
, sl_filemeta_t
);
1329 result
= dalloc_add(reply
, array
, sl_array_t
);
1337 sl_result
= UINT64_MAX
;
1338 result
= dalloc_add_copy(array
, &sl_result
, uint64_t);
1342 result
= dalloc_add(reply
, array
, sl_array_t
);
1353 static bool slrpc_close_query(struct mds_ctx
*mds_ctx
,
1354 const DALLOC_CTX
*query
, DALLOC_CTX
*reply
)
1356 struct sl_query
*slq
= NULL
;
1357 uint64_t *uint64p
, ctx1
, ctx2
;
1362 array
= dalloc_zero(reply
, sl_array_t
);
1363 if (array
== NULL
) {
1368 uint64p
= dalloc_get(query
, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1370 if (uint64p
== NULL
) {
1375 uint64p
= dalloc_get(query
, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1377 if (uint64p
== NULL
) {
1382 /* Get query for context and free it */
1383 slq
= slq_for_ctx(mds_ctx
, ctx1
, ctx2
);
1385 DEBUG(1, ("bad context: [0x%jx,0x%jx]\n",
1386 (uintmax_t)ctx1
, (uintmax_t)ctx2
));
1390 SLQ_DEBUG(10, slq
, "close");
1394 sl_res
= UINT64_MAX
;
1395 result
= dalloc_add_copy(array
, &sl_res
, uint64_t);
1399 result
= dalloc_add(reply
, array
, sl_array_t
);
1406 static struct mdssvc_ctx
*mdssvc_init(struct tevent_context
*ev
)
1410 if (mdssvc_ctx
!= NULL
) {
1414 mdssvc_ctx
= talloc_zero(ev
, struct mdssvc_ctx
);
1415 if (mdssvc_ctx
== NULL
) {
1419 mdssvc_ctx
->ev_ctx
= ev
;
1421 ok
= mdsscv_backend_noindex
.init(mdssvc_ctx
);
1423 DBG_ERR("backend init failed\n");
1424 TALLOC_FREE(mdssvc_ctx
);
1428 #ifdef HAVE_SPOTLIGHT_BACKEND_ES
1429 ok
= mdsscv_backend_es
.init(mdssvc_ctx
);
1431 DBG_ERR("backend init failed\n");
1432 TALLOC_FREE(mdssvc_ctx
);
1437 #ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
1438 ok
= mdsscv_backend_tracker
.init(mdssvc_ctx
);
1440 DBG_ERR("backend init failed\n");
1441 TALLOC_FREE(mdssvc_ctx
);
1450 * Init callbacks at startup
1452 * This gets typically called in the main parent smbd which means we can't
1453 * initialize our global state here.
1455 bool mds_init(struct messaging_context
*msg_ctx
)
1460 bool mds_shutdown(void)
1464 if (mdssvc_ctx
== NULL
) {
1468 ok
= mdsscv_backend_noindex
.shutdown(mdssvc_ctx
);
1473 #ifdef HAVE_SPOTLIGHT_BACKEND_ES
1474 ok
= mdsscv_backend_es
.shutdown(mdssvc_ctx
);
1480 #ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
1481 ok
= mdsscv_backend_tracker
.shutdown(mdssvc_ctx
);
1489 TALLOC_FREE(mdssvc_ctx
);
1494 * Tear down connections and free all resources
1496 static int mds_ctx_destructor_cb(struct mds_ctx
*mds_ctx
)
1499 * We need to free query_list before ino_path_map
1501 while (mds_ctx
->query_list
!= NULL
) {
1503 * slq destructor removes element from list.
1504 * Don't use TALLOC_FREE()!
1506 talloc_free(mds_ctx
->query_list
);
1508 TALLOC_FREE(mds_ctx
->ino_path_map
);
1510 ZERO_STRUCTP(mds_ctx
);
1516 * Initialise a context per RPC bind
1518 * This ends up being called for every tcon, because the client does a
1519 * RPC bind for every tcon, so this is acually a per tcon context.
1521 struct mds_ctx
*mds_init_ctx(TALLOC_CTX
*mem_ctx
,
1522 struct tevent_context
*ev
,
1523 struct auth_session_info
*session_info
,
1525 const char *sharename
,
1528 struct mds_ctx
*mds_ctx
;
1531 smb_iconv_t iconv_hnd
= (smb_iconv_t
)-1;
1533 mds_ctx
= talloc_zero(mem_ctx
, struct mds_ctx
);
1534 if (mds_ctx
== NULL
) {
1537 talloc_set_destructor(mds_ctx
, mds_ctx_destructor_cb
);
1539 mds_ctx
->mdssvc_ctx
= mdssvc_init(ev
);
1540 if (mds_ctx
->mdssvc_ctx
== NULL
) {
1544 backend
= lp_spotlight_backend(snum
);
1545 if (!lp_spotlight(snum
)) {
1546 backend
= SPOTLIGHT_BACKEND_NOINDEX
;
1549 case SPOTLIGHT_BACKEND_NOINDEX
:
1550 mds_ctx
->backend
= &mdsscv_backend_noindex
;
1553 #ifdef HAVE_SPOTLIGHT_BACKEND_ES
1554 case SPOTLIGHT_BACKEND_ES
:
1555 mds_ctx
->backend
= &mdsscv_backend_es
;
1559 #ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
1560 case SPOTLIGHT_BACKEND_TRACKER
:
1561 mds_ctx
->backend
= &mdsscv_backend_tracker
;
1565 DBG_ERR("Unknown backend %d\n", backend
);
1566 TALLOC_FREE(mdssvc_ctx
);
1570 iconv_hnd
= smb_iconv_open_ex(mds_ctx
,
1574 if (iconv_hnd
== (smb_iconv_t
)-1) {
1577 mds_ctx
->ic_nfc_to_nfd
= iconv_hnd
;
1579 iconv_hnd
= smb_iconv_open_ex(mds_ctx
,
1583 if (iconv_hnd
== (smb_iconv_t
)-1) {
1586 mds_ctx
->ic_nfd_to_nfc
= iconv_hnd
;
1588 mds_ctx
->sharename
= talloc_strdup(mds_ctx
, sharename
);
1589 if (mds_ctx
->sharename
== NULL
) {
1593 mds_ctx
->spath
= talloc_strdup(mds_ctx
, path
);
1594 if (mds_ctx
->spath
== NULL
) {
1598 mds_ctx
->snum
= snum
;
1599 mds_ctx
->pipe_session_info
= session_info
;
1601 if (session_info
->security_token
->num_sids
< 1) {
1604 sid_copy(&mds_ctx
->sid
, &session_info
->security_token
->sids
[0]);
1605 mds_ctx
->uid
= session_info
->unix_token
->uid
;
1607 mds_ctx
->ino_path_map
= db_open_rbt(mds_ctx
);
1608 if (mds_ctx
->ino_path_map
== NULL
) {
1609 DEBUG(1,("open inode map db failed\n"));
1613 ok
= mds_ctx
->backend
->connect(mds_ctx
);
1615 DBG_ERR("backend connect failed\n");
1622 if (mds_ctx
->ic_nfc_to_nfd
!= NULL
) {
1623 smb_iconv_close(mds_ctx
->ic_nfc_to_nfd
);
1625 if (mds_ctx
->ic_nfd_to_nfc
!= NULL
) {
1626 smb_iconv_close(mds_ctx
->ic_nfd_to_nfc
);
1629 TALLOC_FREE(mds_ctx
);
1634 * Dispatch a Spotlight RPC command
1636 bool mds_dispatch(struct mds_ctx
*mds_ctx
,
1637 struct mdssvc_blob
*request_blob
,
1638 struct mdssvc_blob
*response_blob
)
1642 DALLOC_CTX
*query
= NULL
;
1643 DALLOC_CTX
*reply
= NULL
;
1645 const struct slrpc_cmd
*slcmd
;
1647 if (CHECK_DEBUGLVL(10)) {
1648 const struct sl_query
*slq
;
1650 for (slq
= mds_ctx
->query_list
; slq
!= NULL
; slq
= slq
->next
) {
1651 SLQ_DEBUG(10, slq
, "pending");
1655 response_blob
->length
= 0;
1657 DEBUG(10, ("share path: %s\n", mds_ctx
->spath
));
1659 query
= dalloc_new(mds_ctx
);
1660 if (query
== NULL
) {
1664 reply
= dalloc_new(mds_ctx
);
1665 if (reply
== NULL
) {
1670 ok
= sl_unpack(query
, (char *)request_blob
->spotlight_blob
,
1671 request_blob
->length
);
1673 DEBUG(1, ("error unpacking Spotlight RPC blob\n"));
1677 DEBUG(5, ("%s", dalloc_dump(query
, 0)));
1679 rpccmd
= dalloc_get(query
, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1681 if (rpccmd
== NULL
) {
1682 DEBUG(1, ("missing primary Spotlight RPC command\n"));
1687 DEBUG(10, ("Spotlight RPC cmd: %s\n", rpccmd
));
1689 slcmd
= slrpc_cmd_by_name(rpccmd
);
1690 if (slcmd
== NULL
) {
1691 DEBUG(1, ("unsupported primary Spotlight RPC command %s\n",
1697 ok
= slcmd
->function(mds_ctx
, query
, reply
);
1699 DBG_DEBUG("%s", dalloc_dump(reply
, 0));
1701 len
= sl_pack(reply
,
1702 (char *)response_blob
->spotlight_blob
,
1703 response_blob
->size
);
1705 DBG_ERR("error packing Spotlight RPC reply\n");
1709 response_blob
->length
= len
;