From: Jeremy Harris Date: Fri, 3 Apr 2020 13:36:17 +0000 (+0100) Subject: dsearch: full-path return option X-Git-Url: https://git.exim.org/users/heiko/exim.git/commitdiff_plain/a5dc727afcc92deab722a84ae5cf3d00ae74c5f6 dsearch: full-path return option --- diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index bfe59fccc..295835dbb 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -6777,12 +6777,24 @@ absolute directory path; this is searched for an entry whose name is the key by calling the &[lstat()]& function. The key may not -contain any forward slash characters. If &[lstat()]& succeeds, the result of -the lookup is the name of the entry, which may be a file, directory, -symbolic link, or any other kind of directory entry. +contain any forward slash characters. +If &[lstat()]& succeeds then so does the lookup. .new +Options for the lookup can be given by appending them after the word "dsearch", +separated by a comma. Options, if present, are a comma-separated list with +each element starting with a tag name and an equals. + +The only option currently supported requests an alternate output value of +the entire path for the entry. Example: +.code +${lookup {passwd} dsearch,ret=full {/etc}} +.endd +The default result is just the requested entry. + +The matching entry may be a file, directory, +symbolic link, or any other kind of directory entry. .cindex "tainted data" "dsearch result" -It is regarded as untainted. +The result is regarded as untainted. .wen An example of how this lookup can be used to support virtual domains is given in section diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index 62763e2ac..9a06feab7 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -47,6 +47,7 @@ Version 4.94 lookup string. The older method fails when tainted variables are used in the lookup, as the filename becomes tainted. The new method keeps the filename separate. +12. An option on the dsearch lookup, to return the full path. diff --git a/src/src/lookups/dsearch.c b/src/src/lookups/dsearch.c index d1cbdf73a..0509a761b 100644 --- a/src/src/lookups/dsearch.c +++ b/src/src/lookups/dsearch.c @@ -64,6 +64,8 @@ return FALSE; * Find entry point * *************************************************/ +#define RET_FULL BIT(0) + /* See local README for interface description. We use lstat() instead of scanning the directory, as it is hopefully faster to let the OS do the scanning for us. */ @@ -76,6 +78,7 @@ dsearch_find(void * handle, const uschar * dirname, const uschar * keystring, struct stat statbuf; int save_errno; uschar * filename; +unsigned flags = 0; handle = handle; /* Keep picky compilers happy */ length = length; @@ -88,12 +91,22 @@ if (Ustrchr(keystring, '/') != 0) return DEFER; } +if (opts) + { + int sep = ','; + uschar * ele; + + while ((ele = string_nextinlist(&opts, &sep, NULL, 0))) + if (Ustrcmp(ele, "ret=full") == 0) + flags |= RET_FULL; + } + filename = string_sprintf("%s/%s", dirname, keystring); if (Ulstat(filename, &statbuf) >= 0) { /* Since the filename exists in the filesystem, we can return a non-tainted result. */ - *result = string_copy_taint(keystring, FALSE); + *result = string_copy_taint(flags & RET_FULL ? filename : keystring, FALSE); return OK; } diff --git a/src/src/search.c b/src/src/search.c index 51bbc6aed..2a60fc78a 100644 --- a/src/src/search.c +++ b/src/src/search.c @@ -172,20 +172,6 @@ if (Ustrncmp(name, "partial", 7) == 0) /* Now we are left with a lookup name, possibly followed by * or *@, and then by options starting with a "," */ -#ifdef old -len = Ustrlen(ss); -if (len >= 2 && Ustrncmp(ss + len - 2, "*@", 2) == 0) - { - *starflags |= SEARCH_STARAT; - len -= 2; - } -else if (len >= 1 && ss[len-1] == '*') - { - *starflags |= SEARCH_STAR; - len--; - } -#endif - len = Ustrlen(ss); if ((t = Ustrchr(ss, '*'))) { @@ -195,7 +181,14 @@ if ((t = Ustrchr(ss, '*'))) else t = ss; -* USS opts = (t = Ustrchr(t, ',')) ? string_copy(t+1) : NULL; +if ((t = Ustrchr(t, ','))) + { + int l = t - ss; + if (l < len) len = l; + *opts = string_copy(t+1); + } +else + * opts = NULL; /* Check for the individual search type. Only those that are actually in the binary are valid. For query-style types, "partial" and default types are @@ -513,6 +506,7 @@ file. No need to check c->item_cache for NULL, tree_search will do so. */ if ( (t = tree_search(c->item_cache, keystring)) && (!(e = t->data.ptr)->expiry || e->expiry > time(NULL)) + && (!opts && !e->opts || opts && e->opts && Ustrcmp(opts, e->opts) == 0) ) { /* Data was in the cache already; set the pointer from the tree node */ data = e->data.ptr; @@ -527,7 +521,8 @@ else DEBUG(D_lookup) { - if (t) debug_printf_indent("cached data found but past valid time; "); + if (t) + debug_printf_indent("cached data found but either wrong opts or dated; "); debug_printf_indent("%s lookup required for %s%s%s\n", filename ? US"file" : US"database", keystring, @@ -555,12 +550,14 @@ else if (t) /* Previous, out-of-date cache entry. Update with the */ { /* new result and forget the old one */ e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache; + e->opts = opts; e->data.ptr = data; } else { e = store_get(sizeof(expiring_data) + sizeof(tree_node) + len, is_tainted(keystring)); e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache; + e->opts = opts; e->data.ptr = data; t = (tree_node *)(e+1); memcpy(t->name, keystring, len); diff --git a/src/src/structs.h b/src/src/structs.h index 7d700fb72..ce6e8e857 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -727,14 +727,16 @@ typedef struct tree_node { /* Structure for holding time-limited data such as DNS returns. We use this rather than extending tree_node to avoid wasting space for most tree use (variables...) at the cost of complexity -for the lookups cache */ +for the lookups cache. +We also store any options used for the lookup. */ typedef struct expiring_data { - time_t expiry; /* if nonzero, data invalid after this time */ + time_t expiry; /* if nonzero, data invalid after this time */ + const uschar * opts; /* options, or NULL */ union { - void *ptr; /* pointer to data */ - int val; /* or integer data */ + void * ptr; /* pointer to data */ + int val; /* or integer data */ } data; } expiring_data; diff --git a/test/scripts/2500-dsearch/2500 b/test/scripts/2500-dsearch/2500 index 040ce599e..14cf31b26 100644 --- a/test/scripts/2500-dsearch/2500 +++ b/test/scripts/2500-dsearch/2500 @@ -7,6 +7,7 @@ fail: ${lookup{TESTNUM.tst} dsearch{DIR/dir_not_here}{$value}{FAIL}} fail(case): ${lookup{TESTNUM.TST} dsearch{DIR/aux-fixed}{$value}{FAIL}} fail(case): ${lookup{TESTNUM.TST} dsearch{DIR/AUX-fixed}{$value}{FAIL}} fail(path): ${lookup{TESTNUM.tst} dsearch{.}{$value}{OTHER}} +ok,full: ${lookup{TESTNUM.tst} dsearch,ret=full {DIR/aux-fixed}{$value}{FAIL}} **** # 1 diff --git a/test/stdout/2500 b/test/stdout/2500 index 3259e726c..d10a5f8a7 100644 --- a/test/stdout/2500 +++ b/test/stdout/2500 @@ -4,4 +4,5 @@ > fail(case): FAIL > Failed: failed to open TESTSUITE/AUX-fixed for directory search: No such file or directory > Failed: dirname '.' for dsearch is not absolute +> ok,full: TESTSUITE/aux-fixed/2500.tst >