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 "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"
32 #include "mdssvc_noindex.h"
33 #ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
34 #include "mdssvc_tracker.h"
36 #ifdef HAVE_SPOTLIGHT_BACKEND_ES
37 #include "mdssvc_es.h"
39 #include "lib/global_contexts.h"
42 #define DBGC_CLASS DBGC_RPC_SRV
46 bool (*function
)(struct mds_ctx
*mds_ctx
,
47 const DALLOC_CTX
*query
,
51 struct slq_destroy_state
{
52 struct tevent_context
*ev
;
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 ************************************************/
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.
97 static bool add_filemeta(struct mds_ctx
*mds_ctx
,
101 const struct stat_ex
*sp
)
105 int i
, metacount
, result
;
109 const char *attribute
;
111 const char *nfc_path
= path
;
113 char *nfd_path
= NULL
;
115 size_t dest_remaining
;
118 metacount
= dalloc_size(reqinfo
);
119 if (metacount
== 0 || path
== NULL
|| sp
== NULL
) {
120 result
= dalloc_add_copy(fm_array
, &nil
, sl_nil_t
);
127 meta
= dalloc_zero(fm_array
, sl_array_t
);
132 nfc_len
= strlen(nfc_path
);
134 * Simple heuristic, strlen by two should give enough room for NFC to
137 nfd_buf_size
= nfc_len
* 2;
138 nfd_path
= talloc_array(meta
, char, nfd_buf_size
);
139 if (nfd_path
== NULL
) {
143 dest_remaining
= talloc_array_length(dest
);
145 nconv
= smb_iconv(mds_ctx
->ic_nfc_to_nfd
,
150 if (nconv
== (size_t)-1) {
154 for (i
= 0; i
< metacount
; i
++) {
155 attribute
= dalloc_get_object(reqinfo
, i
);
156 if (attribute
== NULL
) {
159 if (strcmp(attribute
, "kMDItemDisplayName") == 0
160 || strcmp(attribute
, "kMDItemFSName") == 0) {
161 p
= strrchr(nfd_path
, '/');
163 result
= dalloc_stradd(meta
, p
+ 1);
168 } else if (strcmp(attribute
, "kMDItemPath") == 0) {
169 result
= dalloc_stradd(meta
, nfd_path
);
173 } else if (strcmp(attribute
, "kMDItemFSSize") == 0) {
174 uint64var
= sp
->st_ex_size
;
175 result
= dalloc_add_copy(meta
, &uint64var
, uint64_t);
179 } else if (strcmp(attribute
, "kMDItemFSOwnerUserID") == 0) {
180 uint64var
= sp
->st_ex_uid
;
181 result
= dalloc_add_copy(meta
, &uint64var
, uint64_t);
185 } else if (strcmp(attribute
, "kMDItemFSOwnerGroupID") == 0) {
186 uint64var
= sp
->st_ex_gid
;
187 result
= dalloc_add_copy(meta
, &uint64var
, uint64_t);
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
);
200 result
= dalloc_add_copy(meta
, &nil
, sl_nil_t
);
207 result
= dalloc_add(fm_array
, meta
, sl_array_t
);
214 static int cnid_comp_fn(const void *p1
, const void *p2
)
216 const uint64_t *cnid1
= p1
, *cnid2
= p2
;
217 if (*cnid1
== *cnid2
) {
220 if (*cnid1
< *cnid2
) {
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
;
235 cnids
= talloc_array(slq
, uint64_t, dalloc_size(d
));
240 for (i
= 0; i
< dalloc_size(d
); i
++) {
241 p
= dalloc_get_object(d
, i
);
245 memcpy(&cnids
[i
], p
, sizeof(uint64_t));
247 qsort(cnids
, dalloc_size(d
), sizeof(uint64_t), cnid_comp_fn
);
250 slq
->cnids_num
= dalloc_size(d
);
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
)
262 struct sl_rslts
*query_results
;
265 if (slq
->query_results
) {
266 DEBUG(1, ("unexpected existing result handle\n"));
270 query_results
= talloc_zero(slq
, struct sl_rslts
);
271 if (query_results
== NULL
) {
276 query_results
->cnids
= talloc_zero(query_results
, sl_cnids_t
);
277 if (query_results
->cnids
== NULL
) {
280 query_results
->cnids
->ca_cnids
= dalloc_new(query_results
->cnids
);
281 if (query_results
->cnids
->ca_cnids
== NULL
) {
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
));
290 query_results
->cnids
->ca_context
= (uint32_t)slq
->ctx2
;
293 query_results
->fm_array
= dalloc_zero(query_results
, sl_array_t
);
294 if (query_results
->fm_array
== NULL
) {
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
);
304 slq
->query_results
= query_results
;
308 static bool add_results(sl_array_t
*array
, struct sl_query
*slq
)
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,
320 if (slq
->state
>= SLQ_STATE_DONE
) {
327 fm
= dalloc_zero(array
, sl_filemeta_t
);
332 result
= dalloc_add_copy(array
, &status
, uint64_t);
336 result
= dalloc_add(array
, slq
->query_results
->cnids
, sl_cnids_t
);
340 if (slq
->query_results
->num_results
> 0) {
341 result
= dalloc_add(fm
, slq
->query_results
->fm_array
, sl_array_t
);
346 result
= dalloc_add(array
, fm
, sl_filemeta_t
);
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
);
356 DEBUG(1, ("couldn't add result handle\n"));
357 slq
->state
= SLQ_STATE_ERROR
;
364 static const struct slrpc_cmd
*slrpc_cmd_by_name(const char *rpccmd
)
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
++) {
381 cmp
= strcmp(cmds
[i
].name
, rpccmd
);
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
)
398 for (q
= mds_ctx
->query_list
; q
; q
= q
->next
) {
399 if ((q
->ctx1
== ctx1
) && (q
->ctx2
== ctx2
)) {
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
);
420 TALLOC_FREE(slq
->backend_private
);
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
433 static int ino_path_map_destr_cb(struct sl_inode_path_map
*entry
)
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
)));
446 DBG_DEBUG("deleted [0x%"PRIx64
"] [%s]\n", entry
->ino
, entry
->path
);
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
,
468 struct sl_inode_path_map
*entry
;
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
482 if (value
.dsize
!= sizeof(void *)) {
483 DEBUG(1, ("invalid dsize\n"));
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
);
493 DEBUG(1, ("talloc_reference failed\n"));
499 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
500 DEBUG(1, ("dbwrap_fetch failed %s\n", nt_errstr(status
)));
504 entry
= talloc_zero(slq
->entries_ctx
, struct sl_inode_path_map
);
506 DEBUG(1, ("talloc failed\n"));
511 entry
->mds_ctx
= slq
->mds_ctx
;
513 entry
->path
= talloc_strdup(entry
, path
);
514 if (entry
->path
== NULL
) {
515 DEBUG(1, ("talloc failed\n"));
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
)));
528 talloc_set_destructor(entry
, ino_path_map_destr_cb
);
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
;
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
550 if (!become_authenticated_pipe_user(slq
->mds_ctx
->pipe_session_info
)) {
551 DBG_ERR("can't become authenticated user: %d\n",
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
,
574 if (!NT_STATUS_IS_OK(status
)) {
575 DBG_DEBUG("synthetic_pathref [%s]: %s\n",
576 smb_fname_str_dbg(smb_fname
),
578 unbecome_authenticated_pipe_user();
584 status
= smbd_check_access_rights_fsp(slq
->mds_ctx
->conn
->cwd_fsp
,
588 unbecome_authenticated_pipe_user();
589 if (!NT_STATUS_IS_OK(status
)) {
590 TALLOC_FREE(smb_fname
);
594 /* Done with smb_fname now. */
595 TALLOC_FREE(smb_fname
);
597 ino64
= SMB_VFS_FS_FILE_ID(slq
->mds_ctx
->conn
, &sb
);
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
,
617 sub
= subdir_of(slq
->mds_ctx
->spath
,
618 slq
->mds_ctx
->spath_len
,
622 DBG_ERR("[%s] is not inside [%s]\n",
623 path
, slq
->mds_ctx
->spath
);
624 slq
->state
= SLQ_STATE_ERROR
;
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
,
636 DBG_ERR("dalloc error\n");
637 slq
->state
= SLQ_STATE_ERROR
;
641 fake_path
= talloc_asprintf(slq
,
643 slq
->mds_ctx
->sharename
,
645 if (fake_path
== NULL
) {
646 slq
->state
= SLQ_STATE_ERROR
;
650 ok
= add_filemeta(slq
->mds_ctx
,
652 slq
->query_results
->fm_array
,
656 DBG_ERR("add_filemeta error\n");
657 TALLOC_FREE(fake_path
);
658 slq
->state
= SLQ_STATE_ERROR
;
662 ok
= inode_map_add(slq
, ino64
, fake_path
, &sb
);
663 TALLOC_FREE(fake_path
);
665 DEBUG(1, ("inode_map_add error\n"));
666 slq
->state
= SLQ_STATE_ERROR
;
670 slq
->query_results
->num_results
++;
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
)
689 dict
= dalloc_zero(reply
, sl_dict_t
);
694 /* kMDSStoreHasPersistentUUID = false */
695 result
= dalloc_stradd(dict
, "kMDSStoreHasPersistentUUID");
700 result
= dalloc_add_copy(dict
, &b
, sl_bool_t
);
705 /* kMDSStoreIsBackup = false */
706 result
= dalloc_stradd(dict
, "kMDSStoreIsBackup");
711 result
= dalloc_add_copy(dict
, &b
, sl_bool_t
);
716 /* kMDSStoreUUID = uuid */
717 result
= dalloc_stradd(dict
, "kMDSStoreUUID");
721 memcpy(uuid
.sl_uuid
, "fakeuuidfakeuuid", sizeof(uuid
.sl_uuid
));
722 result
= dalloc_add_copy(dict
, &uuid
, sl_uuid_t
);
727 /* kMDSStoreSupportsVolFS = true */
728 result
= dalloc_stradd(dict
, "kMDSStoreSupportsVolFS");
733 result
= dalloc_add_copy(dict
, &b
, sl_bool_t
);
738 /* kMDSVolumeUUID = uuid */
739 result
= dalloc_stradd(dict
, "kMDSVolumeUUID");
743 memcpy(uuid
.sl_uuid
, "fakeuuidfakeuuid", sizeof(uuid
.sl_uuid
));
744 result
= dalloc_add_copy(dict
, &uuid
, sl_uuid_t
);
749 /* kMDSDiskStoreSpindleNumber = 1 (fake) */
750 result
= dalloc_stradd(dict
, "kMDSDiskStoreSpindleNumber");
755 result
= dalloc_add_copy(dict
, &u
, uint64_t);
760 /* kMDSDiskStorePolicy = 3 (whatever that means, taken from OS X) */
761 result
= dalloc_stradd(dict
, "kMDSDiskStorePolicy");
766 result
= dalloc_add_copy(dict
, &u
, uint64_t);
771 /* kMDSStoreMetaScopes array */
772 result
= dalloc_stradd(dict
, "kMDSStoreMetaScopes");
776 array
= dalloc_zero(dict
, sl_array_t
);
780 result
= dalloc_stradd(array
, "kMDQueryScopeComputer");
784 result
= dalloc_stradd(array
, "kMDQueryScopeAllIndexed");
788 result
= dalloc_stradd(array
, "kMDQueryScopeComputerIndexed");
792 result
= dalloc_add(dict
, array
, sl_array_t
);
797 /* kMDSStoreDevice = 0x1000003 (whatever that means, taken from OS X) */
798 result
= dalloc_stradd(dict
, "kMDSStoreDevice");
803 result
= dalloc_add_copy(dict
, &u
, uint64_t);
808 /* kMDSStoreSupportsTCC = true (whatever that means, taken from OS X) */
809 result
= dalloc_stradd(dict
, "kMDSStoreSupportsTCC");
814 result
= dalloc_add_copy(dict
, &b
, sl_bool_t
);
819 /* kMDSStorePathScopes = ["/"] (whatever that means, taken from OS X) */
820 result
= dalloc_stradd(dict
, "kMDSStorePathScopes");
824 array
= dalloc_zero(dict
, sl_array_t
);
828 s
= talloc_strdup(dict
, "/");
832 talloc_set_name(s
, "smb_ucs2_t *");
833 result
= dalloc_add(array
, s
, smb_ucs2_t
*);
837 result
= dalloc_add(dict
, array
, sl_array_t
);
842 result
= dalloc_add(reply
, dict
, sl_dict_t
);
850 static void slq_close_timer(struct tevent_context
*ev
,
851 struct tevent_timer
*te
,
852 struct timeval current_time
,
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");
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
);
886 slq
->path_scope
= talloc_asprintf(slq
,
889 fake_scope
+ sname_len
+ 1);
890 if (slq
->path_scope
== NULL
) {
897 * Begin a search query
899 static bool slrpc_open_query(struct mds_ctx
*mds_ctx
,
900 const DALLOC_CTX
*query
, DALLOC_CTX
*reply
)
906 sl_array_t
*array
, *path_scope
;
908 struct sl_query
*slq
= NULL
;
910 const char *querystring
= NULL
;
911 size_t querystring_len
;
913 size_t dest_remaining
;
917 array
= dalloc_zero(reply
, sl_array_t
);
922 /* Allocate and initialize query object */
923 slq
= talloc_zero(mds_ctx
, struct sl_query
);
927 slq
->entries_ctx
= talloc_named_const(slq
, 0, "struct sl_query.entries_ctx");
928 if (slq
->entries_ctx
== NULL
) {
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"));
946 querystring
= dalloc_value_for_key(query
, "DALLOC_CTX", 0,
950 if (querystring
== NULL
) {
951 DEBUG(1, ("missing kMDQueryString\n"));
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"));
962 dest
= slq
->query_string
;
963 dest_remaining
= talloc_array_length(dest
);
965 nconv
= smb_iconv(mds_ctx
->ic_nfd_to_nfc
,
970 if (nconv
== (size_t)-1) {
971 DBG_ERR("smb_iconv failed for: %s\n", querystring
);
975 uint64p
= dalloc_get(query
, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
977 if (uint64p
== NULL
) {
980 slq
->ctx1
= *uint64p
;
981 uint64p
= dalloc_get(query
, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
983 if (uint64p
== NULL
) {
986 slq
->ctx2
= *uint64p
;
988 path_scope
= dalloc_value_for_key(query
, "DALLOC_CTX", 0,
992 if (path_scope
== NULL
) {
993 DBG_ERR("missing kMDScopeArray\n");
997 scope
= dalloc_get(path_scope
, "char *", 0);
999 scope
= dalloc_get(path_scope
,
1003 if (scope
== NULL
) {
1004 DBG_ERR("Failed to parse kMDScopeArray\n");
1008 ok
= mdssvc_real_scope(slq
, scope
);
1013 reqinfo
= dalloc_value_for_key(query
, "DALLOC_CTX", 0,
1015 "kMDAttributeArray",
1017 if (reqinfo
== NULL
) {
1018 DBG_ERR("missing kMDAttributeArray\n");
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,
1027 "kMDQueryItemArray",
1030 ok
= sort_cnids(slq
, cnids
->ca_cnids
);
1036 ok
= create_result_handle(slq
);
1038 DEBUG(1, ("create_result_handle error\n"));
1039 slq
->state
= SLQ_STATE_ERROR
;
1043 SLQ_DEBUG(10, slq
, "new");
1045 DLIST_ADD(mds_ctx
->query_list
, slq
);
1047 ok
= mds_ctx
->backend
->search_start(slq
);
1049 DBG_ERR("backend search_start failed\n");
1054 result
= dalloc_add_copy(array
, &sl_result
, uint64_t);
1058 result
= dalloc_add(reply
, array
, sl_array_t
);
1065 sl_result
= UINT64_MAX
;
1067 result
= dalloc_add_copy(array
, &sl_result
, uint64_t);
1071 result
= dalloc_add(reply
, array
, sl_array_t
);
1079 * Fetch results of a query
1081 static bool slrpc_fetch_query_results(struct mds_ctx
*mds_ctx
,
1082 const DALLOC_CTX
*query
,
1086 struct sl_query
*slq
= NULL
;
1087 uint64_t *uint64p
, ctx1
, ctx2
;
1092 array
= dalloc_zero(reply
, sl_array_t
);
1093 if (array
== NULL
) {
1097 /* Get query for context */
1098 uint64p
= dalloc_get(query
, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1100 if (uint64p
== NULL
) {
1105 uint64p
= dalloc_get(query
, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1107 if (uint64p
== NULL
) {
1112 slq
= slq_for_ctx(mds_ctx
, ctx1
, ctx2
);
1114 DEBUG(1, ("bad context: [0x%jx,0x%jx]\n",
1115 (uintmax_t)ctx1
, (uintmax_t)ctx2
));
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"));
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
);
1138 DEBUG(1, ("error adding results\n"));
1141 if (slq
->state
== SLQ_STATE_FULL
) {
1142 slq
->state
= SLQ_STATE_RUNNING
;
1143 slq
->mds_ctx
->backend
->search_cont(slq
);
1147 case SLQ_STATE_ERROR
:
1148 DEBUG(1, ("query in error state\n"));
1152 DEBUG(1, ("unexpected query state %d\n", slq
->state
));
1156 result
= dalloc_add(reply
, array
, sl_array_t
);
1163 status
= UINT64_MAX
;
1165 result
= dalloc_add_copy(array
, &status
, uint64_t);
1169 result
= dalloc_add(reply
, array
, sl_array_t
);
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
)
1186 array
= dalloc_zero(reply
, sl_array_t
);
1187 if (array
== NULL
) {
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
1199 result
= dalloc_add_copy(array
, &sl_result
, uint64_t);
1203 result
= dalloc_add(reply
, array
, sl_array_t
);
1212 * Fetch supported metadata attributes for a CNID
1214 static bool slrpc_fetch_attributenames(struct mds_ctx
*mds_ctx
,
1215 const DALLOC_CTX
*query
,
1222 sl_cnids_t
*replycnids
;
1223 sl_array_t
*mdattrs
;
1224 sl_filemeta_t
*fmeta
;
1228 cnids
= dalloc_get(query
, "DALLOC_CTX", 0, "sl_cnids_t", 1);
1229 if (cnids
== NULL
) {
1233 p
= dalloc_get_object(cnids
->ca_cnids
, 0);
1237 memcpy(&id
, p
, sizeof(uint64_t));
1240 array
= dalloc_zero(reply
, sl_array_t
);
1241 if (array
== NULL
) {
1245 result
= dalloc_add(reply
, array
, sl_array_t
);
1250 /* Return result value 0 */
1252 result
= dalloc_add_copy(array
, &sl_result
, uint64_t);
1257 /* Return CNID array */
1258 replycnids
= talloc_zero(reply
, sl_cnids_t
);
1259 if (replycnids
== NULL
) {
1263 replycnids
->ca_cnids
= dalloc_new(cnids
);
1264 if (replycnids
->ca_cnids
== NULL
) {
1268 replycnids
->ca_unkn1
= 0xfec;
1269 replycnids
->ca_context
= cnids
->ca_context
;
1270 result
= dalloc_add_copy(replycnids
->ca_cnids
, &id
, uint64_t);
1274 result
= dalloc_add(array
, replycnids
, sl_cnids_t
);
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
) {
1288 result
= dalloc_stradd(mdattrs
, "kMDItemFSName");
1292 result
= dalloc_stradd(mdattrs
, "kMDItemDisplayName");
1296 result
= dalloc_stradd(mdattrs
, "kMDItemFSSize");
1300 result
= dalloc_stradd(mdattrs
, "kMDItemFSOwnerUserID");
1304 result
= dalloc_stradd(mdattrs
, "kMDItemFSOwnerGroupID");
1308 result
= dalloc_stradd(mdattrs
, "kMDItemFSContentChangeDate");
1313 fmeta
= dalloc_zero(reply
, sl_filemeta_t
);
1314 if (fmeta
== NULL
) {
1317 result
= dalloc_add(fmeta
, mdattrs
, sl_array_t
);
1321 result
= dalloc_add(array
, fmeta
, sl_filemeta_t
);
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
)
1339 sl_cnids_t
*replycnids
;
1340 sl_array_t
*reqinfo
;
1344 sl_array_t
*fm_array
;
1347 struct smb_filename
*smb_fname
= NULL
;
1348 struct stat_ex
*sp
= NULL
;
1349 struct sl_inode_path_map
*elem
= NULL
;
1351 TDB_DATA val
= tdb_null
;
1354 array
= dalloc_zero(reply
, sl_array_t
);
1355 if (array
== NULL
) {
1358 replycnids
= talloc_zero(reply
, sl_cnids_t
);
1359 if (replycnids
== NULL
) {
1362 replycnids
->ca_cnids
= dalloc_new(replycnids
);
1363 if (replycnids
->ca_cnids
== NULL
) {
1366 fm
= dalloc_zero(array
, sl_filemeta_t
);
1370 fm_array
= dalloc_zero(fm
, sl_array_t
);
1371 if (fm_array
== NULL
) {
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
);
1380 reqinfo
= dalloc_get(query
, "DALLOC_CTX", 0, "sl_array_t", 1);
1381 if (reqinfo
== NULL
) {
1385 cnids
= dalloc_get(query
, "DALLOC_CTX", 0, "sl_cnids_t", 2);
1386 if (cnids
== NULL
) {
1389 p
= dalloc_get_object(cnids
->ca_cnids
, 0);
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);
1402 status
= dbwrap_fetch(mds_ctx
->ino_path_map
, reply
,
1403 make_tdb_data((void*)&ino
, sizeof(uint64_t)),
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
);
1412 memcpy(&p
, val
.dptr
, sizeof(p
));
1413 elem
= talloc_get_type_abort(p
, struct sl_inode_path_map
);
1419 ok
= add_filemeta(mds_ctx
, reqinfo
, fm_array
, path
, sp
);
1425 result
= dalloc_add_copy(array
, &sl_result
, uint64_t);
1429 result
= dalloc_add(array
, replycnids
, sl_cnids_t
);
1433 result
= dalloc_add(fm
, fm_array
, sl_array_t
);
1437 result
= dalloc_add(array
, fm
, sl_filemeta_t
);
1441 result
= dalloc_add(reply
, array
, sl_array_t
);
1446 TALLOC_FREE(smb_fname
);
1451 TALLOC_FREE(smb_fname
);
1452 sl_result
= UINT64_MAX
;
1453 result
= dalloc_add_copy(array
, &sl_result
, uint64_t);
1457 result
= dalloc_add(reply
, array
, sl_array_t
);
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
;
1477 array
= dalloc_zero(reply
, sl_array_t
);
1478 if (array
== NULL
) {
1483 uint64p
= dalloc_get(query
, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1485 if (uint64p
== NULL
) {
1490 uint64p
= dalloc_get(query
, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1492 if (uint64p
== NULL
) {
1497 /* Get query for context and free it */
1498 slq
= slq_for_ctx(mds_ctx
, ctx1
, ctx2
);
1500 DEBUG(1, ("bad context: [0x%jx,0x%jx]\n",
1501 (uintmax_t)ctx1
, (uintmax_t)ctx2
));
1505 SLQ_DEBUG(10, slq
, "close");
1509 sl_res
= UINT64_MAX
;
1510 result
= dalloc_add_copy(array
, &sl_res
, uint64_t);
1514 result
= dalloc_add(reply
, array
, sl_array_t
);
1521 static struct mdssvc_ctx
*mdssvc_init(struct tevent_context
*ev
)
1525 if (mdssvc_ctx
!= NULL
) {
1529 mdssvc_ctx
= talloc_zero(ev
, struct mdssvc_ctx
);
1530 if (mdssvc_ctx
== NULL
) {
1534 mdssvc_ctx
->ev_ctx
= ev
;
1536 ok
= mdsscv_backend_noindex
.init(mdssvc_ctx
);
1538 DBG_ERR("backend init failed\n");
1539 TALLOC_FREE(mdssvc_ctx
);
1543 #ifdef HAVE_SPOTLIGHT_BACKEND_ES
1544 ok
= mdsscv_backend_es
.init(mdssvc_ctx
);
1546 DBG_ERR("backend init failed\n");
1547 TALLOC_FREE(mdssvc_ctx
);
1552 #ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
1553 ok
= mdsscv_backend_tracker
.init(mdssvc_ctx
);
1555 DBG_ERR("backend init failed\n");
1556 TALLOC_FREE(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
)
1575 bool mds_shutdown(void)
1579 if (mdssvc_ctx
== NULL
) {
1583 ok
= mdsscv_backend_noindex
.shutdown(mdssvc_ctx
);
1588 #ifdef HAVE_SPOTLIGHT_BACKEND_ES
1589 ok
= mdsscv_backend_es
.shutdown(mdssvc_ctx
);
1595 #ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
1596 ok
= mdsscv_backend_tracker
.shutdown(mdssvc_ctx
);
1604 TALLOC_FREE(mdssvc_ctx
);
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
);
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
,
1646 const char *sharename
,
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
;
1657 smb_iconv_t iconv_hnd
= (smb_iconv_t
)-1;
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
);
1677 case SPOTLIGHT_BACKEND_NOINDEX
:
1678 mds_ctx
->backend
= &mdsscv_backend_noindex
;
1681 #ifdef HAVE_SPOTLIGHT_BACKEND_ES
1682 case SPOTLIGHT_BACKEND_ES
:
1683 mds_ctx
->backend
= &mdsscv_backend_es
;
1687 #ifdef HAVE_SPOTLIGHT_BACKEND_TRACKER
1688 case SPOTLIGHT_BACKEND_TRACKER
:
1689 mds_ctx
->backend
= &mdsscv_backend_tracker
;
1693 DBG_ERR("Unknown backend %d\n", backend
);
1694 TALLOC_FREE(mdssvc_ctx
);
1695 status
= NT_STATUS_INTERNAL_ERROR
;
1699 iconv_hnd
= smb_iconv_open_ex(mds_ctx
,
1703 if (iconv_hnd
== (smb_iconv_t
)-1) {
1704 status
= NT_STATUS_INTERNAL_ERROR
;
1707 mds_ctx
->ic_nfc_to_nfd
= iconv_hnd
;
1709 iconv_hnd
= smb_iconv_open_ex(mds_ctx
,
1713 if (iconv_hnd
== (smb_iconv_t
)-1) {
1714 status
= NT_STATUS_INTERNAL_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
;
1725 mds_ctx
->spath
= talloc_strdup(mds_ctx
, path
);
1726 if (mds_ctx
->spath
== NULL
) {
1727 status
= NT_STATUS_NO_MEMORY
;
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
;
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
;
1749 status
= create_conn_struct_cwd(mds_ctx
,
1754 lp_path(talloc_tos(), lp_sub
, snum
),
1756 if (!NT_STATUS_IS_OK(status
)) {
1757 DBG_ERR("failed to create conn for vfs: %s\n",
1762 conn_basedir
= (struct smb_filename
) {
1763 .base_name
= mds_ctx
->conn
->connectpath
,
1766 ret
= vfs_ChDir(mds_ctx
->conn
, &conn_basedir
);
1768 DBG_ERR("vfs_ChDir [%s] failed: %s\n",
1769 conn_basedir
.base_name
, strerror(errno
));
1770 status
= map_nt_error_from_unix(errno
);
1774 ok
= mds_ctx
->backend
->connect(mds_ctx
);
1776 DBG_ERR("backend connect failed\n");
1777 status
= NT_STATUS_CONNECTION_RESET
;
1781 *_mds_ctx
= mds_ctx
;
1782 return NT_STATUS_OK
;
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
);
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
)
1806 DALLOC_CTX
*query
= NULL
;
1807 DALLOC_CTX
*reply
= NULL
;
1809 const struct slrpc_cmd
*slcmd
;
1810 const struct smb_filename conn_basedir
= {
1811 .base_name
= mds_ctx
->conn
->connectpath
,
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
) {
1832 reply
= dalloc_new(mds_ctx
);
1833 if (reply
== NULL
) {
1838 ok
= sl_unpack(query
, (char *)request_blob
->spotlight_blob
,
1839 request_blob
->length
);
1841 DEBUG(1, ("error unpacking Spotlight RPC blob\n"));
1845 DEBUG(5, ("%s", dalloc_dump(query
, 0)));
1847 rpccmd
= dalloc_get(query
, "DALLOC_CTX", 0, "DALLOC_CTX", 0,
1849 if (rpccmd
== NULL
) {
1850 DEBUG(1, ("missing primary Spotlight RPC command\n"));
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",
1865 ret
= vfs_ChDir(mds_ctx
->conn
, &conn_basedir
);
1867 DBG_ERR("vfs_ChDir [%s] failed: %s\n",
1868 conn_basedir
.base_name
, strerror(errno
));
1873 ok
= slcmd
->function(mds_ctx
, query
, reply
);
1878 DBG_DEBUG("%s", dalloc_dump(reply
, 0));
1880 status
= sl_pack_alloc(response_blob
,
1884 if (!NT_STATUS_IS_OK(status
)) {
1885 DBG_ERR("sl_pack_alloc() failed\n");