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
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.
* 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. */
struct stat statbuf;
int save_errno;
uschar * filename;
+unsigned flags = 0;
handle = handle; /* Keep picky compilers happy */
length = length;
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;
}
/* 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, '*')))
{
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
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;
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,
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);
/* 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;
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
> 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
>