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