diff --git a/etc/afpd/spotlight.c b/etc/afpd/spotlight.c index 3df6866b960..a639aa345ee 100644 --- a/etc/afpd/spotlight.c +++ b/etc/afpd/spotlight.c @@ -352,18 +352,32 @@ static bool add_filemeta(sl_array_t *reqinfo, } meta = talloc_zero(fm_array, sl_array_t); + /* + * macOS expects filenames and paths in NFD (decomposed) form. + * Convert the NFC server-side path to NFD before returning string + * attributes. Fall back to the original path if conversion fails. + */ + size_t path_len = strlen(path); + size_t nfd_buf_size = path_len * 3 + 1; + char *nfd_path = talloc_array(meta, char, nfd_buf_size); + + if (nfd_path == NULL + || charset_decompose(CH_UTF8, (char *)path, path_len, + nfd_path, nfd_buf_size) == (size_t) -1) { + nfd_path = (char *)path; + } for (i = 0; i < metacount; i++) { if (strequal(reqinfo->dd_talloc_array[i], "kMDItemDisplayName") || strequal(reqinfo->dd_talloc_array[i], "kMDItemFSName") || strequal(reqinfo->dd_talloc_array[i], "_kMDItemFileName")) { - if ((p = strrchr(path, '/'))) { + if ((p = strrchr(nfd_path, '/'))) { name = dalloc_strdup(meta, p + 1); dalloc_add(meta, name, "char *"); } } else if (strequal(reqinfo->dd_talloc_array[i], "kMDItemPath")) { - name = dalloc_strdup(meta, path); + name = dalloc_strdup(meta, nfd_path); dalloc_add(meta, name, "char *"); } else if (strequal(reqinfo->dd_talloc_array[i], "kMDItemFSSize") || strequal(reqinfo->dd_talloc_array[i], "kMDItemLogicalSize")) { @@ -378,13 +392,23 @@ static bool add_filemeta(sl_array_t *reqinfo, uint64var = sp->st_gid; dalloc_add_copy(meta, &uint64var, uint64_t); } else if (strequal(reqinfo->dd_talloc_array[i], - "kMDItemFSContentChangeDate")) { + "kMDItemFSContentChangeDate") + || strequal(reqinfo->dd_talloc_array[i], + "kMDItemContentModificationDate")) { sl_time = convert_timespec_to_timeval(sp->st_mtim); dalloc_add_copy(meta, &sl_time, sl_time_t); } else if (strequal(reqinfo->dd_talloc_array[i], "kMDItemLastUsedDate")) { sl_time = convert_timespec_to_timeval(sp->st_atim); dalloc_add_copy(meta, &sl_time, sl_time_t); + } else if (strequal(reqinfo->dd_talloc_array[i], + "kMDItemContentCreationDate")) { +#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC + sl_time = convert_timespec_to_timeval(sp->st_birthtimespec); + dalloc_add_copy(meta, &sl_time, sl_time_t); +#else + dalloc_add_copy(meta, &nil, sl_nil_t); +#endif } else { dalloc_add_copy(meta, &nil, sl_nil_t); } @@ -449,19 +473,21 @@ static bool add_results(sl_array_t *array, slq_t *slq) return false; } - switch (slq->slq_state) { - case SLQ_STATE_RUNNING: - /* - * Wtf, why 35? Taken from an AFP capture. - */ - status = 35; - break; - - default: + /* + * Status 35 (0x23) signals "results pending, poll again". + * All states prior to DONE (RUNNING, RESULTS, FULL) return 0x23; + * DONE and beyond return 0. + */ + if (slq->slq_state >= SLQ_STATE_DONE) { status = 0; - break; + } else { + status = 35; } + LOG(log_debug, logtype_sl, + "dispatching %d result(s) to client, status %" PRIu64 " (ctx1: %" PRIx64 + ", ctx2: %" PRIx64 ")", + slq->query_results->num_results, status, slq->slq_ctx1, slq->slq_ctx2); dalloc_add_copy(array, &status, uint64_t); dalloc_add(array, slq->query_results->cnids, sl_cnids_t); @@ -682,11 +708,19 @@ static void tracker_cursor_cb(GObject *object, gboolean more_results; const gchar *uri; char *path; - int result; struct stat sb; uint64_t uint64var; bool ok; cnid_t did, id; + + if (slq->query_results == NULL) { + LOG(log_error, logtype_sl, + "cursor cb: ctx1: %" PRIx64 ", ctx2: %" PRIx64 ": no result handle", + slq->slq_ctx1, slq->slq_ctx2); + slq->slq_state = SLQ_STATE_ERROR; + return; + } + LOG(log_debug, logtype_sl, "cursor cb[%d]: ctx1: %" PRIx64 ", ctx2: %" PRIx64, slq->query_results->num_results, slq->slq_ctx1, slq->slq_ctx2); @@ -736,16 +770,21 @@ static void tracker_cursor_cb(GObject *object, return; } - result = access(path, R_OK); + if (stat(path, &sb) != 0) { + LOG(log_debug, logtype_sl, "skipping result, stat failed: %s", path); + goto exit; + } - if (result != 0) { + if (access(path, R_OK) != 0) { + LOG(log_debug, logtype_sl, "skipping result, access denied: %s", path); goto exit; } id = cnid_for_path(slq->slq_vol->v_cdb, slq->slq_vol->v_path, path, &did); if (id == CNID_INVALID) { - LOG(log_error, logtype_sl, "cnid_for_path error: %s", path); + LOG(log_debug, logtype_sl, + "skipping result, no CNID (file moved or deleted?): %s", path); goto exit; } @@ -756,10 +795,14 @@ static void tracker_cursor_cb(GObject *object, sizeof(uint64_t), cnid_comp_fn); if (!ok) { + LOG(log_debug, logtype_sl, "skipping result, CNID not in client filter: %s", + path); goto exit; } } + LOG(log_debug, logtype_sl, "adding result CNID %" PRIu32 ": %s", + ntohl(id), path); dalloc_add_copy(slq->query_results->cnids->ca_cnids, &uint64var, uint64_t); ok = add_filemeta(slq->slq_reqinfo, slq->query_results->fm_array, @@ -844,29 +887,48 @@ static int sl_rpc_fetchPropertiesForContext(const AFPObj *obj, } dict = talloc_zero(reply, sl_dict_t); - /* key/val 1 */ + sl_bool_t b; + /* kMDSStoreMetaScopes */ s = dalloc_strdup(dict, "kMDSStoreMetaScopes"); dalloc_add(dict, s, char *); array = talloc_zero(dict, sl_array_t); s = dalloc_strdup(array, "kMDQueryScopeComputer"); dalloc_add(array, s, char *); + s = dalloc_strdup(array, "kMDQueryScopeAllIndexed"); + dalloc_add(array, s, char *); + s = dalloc_strdup(array, "kMDQueryScopeComputerIndexed"); + dalloc_add(array, s, char *); dalloc_add(dict, array, sl_array_t); - /* key/val 2 */ + /* kMDSStorePathScopes */ s = dalloc_strdup(dict, "kMDSStorePathScopes"); dalloc_add(dict, s, char *); array = talloc_zero(dict, sl_array_t); s = dalloc_strdup(array, v->v_path); dalloc_add(array, s, char *); dalloc_add(dict, array, sl_array_t); - /* key/val 3 */ + /* kMDSStoreUUID */ s = dalloc_strdup(dict, "kMDSStoreUUID"); dalloc_add(dict, s, char *); memcpy(uuid.sl_uuid, v->v_uuid, 16); dalloc_add_copy(dict, &uuid, sl_uuid_t); - /* key/val 4 */ + /* kMDSVolumeUUID */ + s = dalloc_strdup(dict, "kMDSVolumeUUID"); + dalloc_add(dict, s, char *); + dalloc_add_copy(dict, &uuid, sl_uuid_t); + /* kMDSStoreHasPersistentUUID */ s = dalloc_strdup(dict, "kMDSStoreHasPersistentUUID"); dalloc_add(dict, s, char *); - sl_bool_t b = true; + b = true; + dalloc_add_copy(dict, &b, sl_bool_t); + /* kMDSStoreIsBackup */ + s = dalloc_strdup(dict, "kMDSStoreIsBackup"); + dalloc_add(dict, s, char *); + b = (v->v_flags & AFPVOL_TM) ? true : false; + dalloc_add_copy(dict, &b, sl_bool_t); + /* kMDSStoreSupportsVolFS */ + s = dalloc_strdup(dict, "kMDSStoreSupportsVolFS"); + dalloc_add(dict, s, char *); + b = true; dalloc_add_copy(dict, &b, sl_bool_t); dalloc_add(reply, dict, sl_dict_t); EC_CLEANUP: @@ -1064,10 +1126,15 @@ static int sl_rpc_fetchQueryResultsForContext(const AFPObj *obj, } ctx2 = *uint64; + LOG(log_debug, logtype_sl, + "fetchQueryResults: ctx1: %" PRIx64 ", ctx2: %" PRIx64, ctx1, ctx2); /* Get query for context */ slq = slq_for_ctx(ctx1, ctx2); if (slq == NULL) { + LOG(log_error, logtype_sl, + "fetchQueryResults: no query for ctx1: %" PRIx64 ", ctx2: %" PRIx64, ctx1, + ctx2); EC_FAIL; } @@ -1498,8 +1565,7 @@ int afp_spotlight_rpc(AFPObj *obj, char *ibuf, size_t ibuflen, case SPOTLIGHT_CMD_OPEN2: RSIVAL(rbuf, 0, ntohs(vid)); RSIVAL(rbuf, 4, 0); - len = strlen(vol->v_path) + 1; - strlcpy(rbuf + 8, vol->v_path, len); + len = (int)strlcpy(rbuf + 8, vol->v_path, MAXPATHLEN) + 1; *rbuflen += 8 + len; break; @@ -1528,7 +1594,8 @@ int afp_spotlight_rpc(AFPObj *obj, char *ibuf, size_t ibuflen, EC_ZERO_LOG(sl_rpc_storeAttributesForOIDArray(obj, query, reply, vol)); } else if (STRCMP(rpccmd, ==, "fetchAttributeNamesForOIDArray:context:")) { EC_ZERO_LOG(sl_rpc_fetchAttributeNamesForOIDArray(obj, query, reply, vol)); - } else if (STRCMP(rpccmd, ==, "fetchAttributes:forOIDArray:context:")) { + } else if (STRCMP(rpccmd, ==, "fetchAttributes:forOIDArray:context:") + || STRCMP(rpccmd, ==, "fetchAllAttributes:forOIDArray:context:")) { EC_ZERO_LOG(sl_rpc_fetchAttributesForOIDArray(obj, query, reply, vol)); } else if (STRCMP(rpccmd, ==, "closeQueryForContext:")) { EC_ZERO_LOG(sl_rpc_closeQueryForContext(obj, query, reply, vol)); diff --git a/etc/afpd/spotlight_marshalling.c b/etc/afpd/spotlight_marshalling.c index 518bf0db9db..78d75659fda 100644 --- a/etc/afpd/spotlight_marshalling.c +++ b/etc/afpd/spotlight_marshalling.c @@ -43,7 +43,7 @@ * RPC data marshalling and unmarshalling **************************************************************************************************/ - /* Spotlight epoch is 1.1.2001 00:00 UTC */ +/* Spotlight epoch is 1.1.2001 00:00 UTC */ #define SPOTLIGHT_TIME_DELTA 978307200 /* Diff from UNIX epoch to Spotlight epoch */ #define SQ_TYPE_NULL 0x0000 @@ -503,8 +503,7 @@ static int sl_unpack_date(DALLOC_CTX *query, const char *buf, int offset, query_data64 = sl_unpack_uint64(buf, offset, encoding); ieee_fp_union.w = query_data64; fraction = ieee_fp_union.d - (uint64_t)ieee_fp_union.d; - - t = (sl_time_t){ + t = (sl_time_t) { .tv_sec = ieee_fp_union.d + SPOTLIGHT_TIME_DELTA, .tv_usec = fraction * 1000000 }; diff --git a/include/atalk/dalloc.h b/include/atalk/dalloc.h index 64f43df02c6..9b6231eee96 100644 --- a/include/atalk/dalloc.h +++ b/include/atalk/dalloc.h @@ -30,8 +30,8 @@ typedef struct { extern int dalloc_add_talloc_chunk(DALLOC_CTX *dd, void *talloc_chunk, void *obj, size_t size); -#define dalloc_add_copy(d, obj, type) dalloc_add_talloc_chunk((d), talloc((d), type), (obj), sizeof(type)); -#define dalloc_add(d, obj, type) dalloc_add_talloc_chunk((d), NULL, (obj), 0); +#define dalloc_add_copy(d, obj, type) dalloc_add_talloc_chunk((d), talloc((d), type), (obj), sizeof(type)) +#define dalloc_add(d, obj, type) dalloc_add_talloc_chunk((d), NULL, (obj), 0) extern void *dalloc_get(const DALLOC_CTX *d, ...); extern void *dalloc_value_for_key(const DALLOC_CTX *d, ...); extern char *dalloc_strdup(const void *ctx, const char *string); diff --git a/meson.build b/meson.build index ae9a1973b80..2bb88720b8a 100644 --- a/meson.build +++ b/meson.build @@ -352,6 +352,9 @@ endif if cc.has_member('struct tm', 'tm_gmtoff', prefix: '#include ') cdata.set('HAVE_STRUCT_TM_TM_GMTOFF', 1) endif +if cc.has_member('struct stat', 'st_birthtimespec', prefix: '#include ') + cdata.set('HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC', 1) +endif ################# # Configuration #