Gitweb:
https://git.exim.org/exim.git/commitdiff/a5dc727afcc92deab722a84ae5cf3d00ae74c5f6
Commit: a5dc727afcc92deab722a84ae5cf3d00ae74c5f6
Parent: 67a57a5afd313490a8763d60ec4df857b9cf239b
Author: Jeremy Harris <jgh146exb@???>
AuthorDate: Fri Apr 3 14:36:17 2020 +0100
Committer: Jeremy Harris <jgh146exb@???>
CommitDate: Fri Apr 3 14:36:17 2020 +0100
dsearch: full-path return option
---
doc/doc-docbook/spec.xfpt | 20 ++++++++++++++++----
doc/doc-txt/NewStuff | 1 +
src/src/lookups/dsearch.c | 15 ++++++++++++++-
src/src/search.c | 29 +++++++++++++----------------
src/src/structs.h | 10 ++++++----
test/scripts/2500-dsearch/2500 | 1 +
test/stdout/2500 | 1 +
7 files changed, 52 insertions(+), 25 deletions(-)
diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt
index bfe59fc..295835d 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 62763e2..9a06fea 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 d1cbdf7..0509a76 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 51bbc6a..2a60fc7 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 7d700fb..ce6e8e8 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 040ce59..14cf31b 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 3259e72..d10a5f8 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
>