s3:lib: add a new samba_path_matching* infrastructure
authorStefan Metzmacher <metze@samba.org>
Wed, 9 Jun 2021 12:44:39 +0000 (14:44 +0200)
committerStefan Metzmacher <metze@samba.org>
Thu, 1 Jul 2021 13:02:31 +0000 (13:02 +0000)
This aims to replace the current is_in_path() code in the long run.

For now it implements samba_path_matching_mswild_create()
in order to replace is_in_path() in the long run.

But there will be other "backends" using regexec() too.

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
source3/lib/util_matching.c [new file with mode: 0644]
source3/lib/util_matching.h [new file with mode: 0644]
source3/torture/test_matching.c
source3/wscript_build

diff --git a/source3/lib/util_matching.c b/source3/lib/util_matching.c
new file mode 100644 (file)
index 0000000..a11ff49
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+   Unix SMB/CIFS implementation.
+   Samba utility functions
+   Copyright (C) Stefan Metzmacher 2021
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/util_matching.h"
+#include "lib/util/string_wrappers.h"
+
+struct samba_path_matching_entry {
+       const char *name;
+       bool is_wild;
+};
+
+struct samba_path_matching_result {
+       ssize_t replace_start;
+       ssize_t replace_end;
+       bool match;
+};
+
+struct samba_path_matching {
+       bool case_sensitive;
+       NTSTATUS (*matching_fn)(const struct samba_path_matching *pm,
+                               const struct samba_path_matching_entry *e,
+                               const char *namecomponent,
+                               struct samba_path_matching_result *result);
+       size_t num_entries;
+       struct samba_path_matching_entry *entries;
+};
+
+static NTSTATUS samba_path_matching_split(TALLOC_CTX *mem_ctx,
+                                         const char *namelist_in,
+                                         struct samba_path_matching **ppm)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       char *name_end = NULL;
+       char *namelist = NULL;
+       char *namelist_end = NULL;
+       char *nameptr = NULL;
+       struct samba_path_matching *pm = NULL;
+       size_t num_entries = 0;
+       struct samba_path_matching_entry *entries = NULL;
+
+       *ppm = NULL;
+
+       pm = talloc_zero(mem_ctx, struct samba_path_matching);
+       if (pm == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+       talloc_reparent(mem_ctx, frame, pm);
+
+       namelist = talloc_strdup(frame, namelist_in);
+       if (namelist == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+       nameptr = namelist;
+
+       namelist_end = &namelist[strlen(namelist)];
+
+       /*
+        * We need to make two passes over the string. The
+        * first to count the number of elements, the second
+        * to split it.
+        *
+        * The 1st time entries is NULL.
+        * the 2nd time entries is allocated.
+        */
+again:
+       while (nameptr <= namelist_end) {
+               /* anything left? */
+               if (*nameptr == '\0') {
+                       break;
+               }
+
+               if (*nameptr == '/') {
+                       /* cope with multiple (useless) /s) */
+                       nameptr++;
+                       continue;
+               }
+
+               /* find the next '/' or consume remaining */
+               name_end = strchr_m(nameptr, '/');
+               if (entries != NULL) {
+                       if (name_end != NULL) {
+                               *name_end = '\0';
+                       }
+                       entries[num_entries].name = talloc_strdup(entries,
+                                                                 nameptr);
+                       if (entries[num_entries].name == NULL) {
+                               TALLOC_FREE(frame);
+                               return NT_STATUS_NO_MEMORY;
+                       }
+               }
+               num_entries++;
+               if (name_end != NULL) {
+                       /* next segment please */
+                       nameptr = name_end + 1;
+                       continue;
+               }
+
+               /* no entries remaining */
+               break;
+       }
+
+       if (num_entries == 0) {
+               /*
+                * No entries in the first round => we're done
+                */
+               goto done;
+       }
+
+       if (entries != NULL) {
+               /*
+                * We finished the 2nd round => we're done
+                */
+               goto done;
+       }
+
+       /*
+        * Now allocate the array and loop again
+        * in order to split the names.
+        */
+       entries = talloc_zero_array(pm,
+                                   struct samba_path_matching_entry,
+                                   num_entries);
+       if (entries == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+       num_entries = 0;
+       nameptr = namelist;
+       goto again;
+
+done:
+       pm->num_entries = num_entries;
+       pm->entries = entries;
+       *ppm = talloc_move(mem_ctx, &pm);
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
+};
+
+static NTSTATUS samba_path_create_mswild_fn(const struct samba_path_matching *pm,
+                                           const struct samba_path_matching_entry *e,
+                                           const char *namecomponent,
+                                           struct samba_path_matching_result *result)
+{
+       bool match = false;
+
+       if (e->is_wild) {
+               match = mask_match(namecomponent, e->name, pm->case_sensitive);
+       } else if (pm->case_sensitive) {
+               match = (strcmp(namecomponent, e->name) == 0);
+       } else {
+               match = (strcasecmp_m(namecomponent, e->name) == 0);
+       }
+
+       *result = (struct samba_path_matching_result) {
+               .match = match,
+               .replace_start = -1,
+               .replace_end = -1,
+       };
+
+       return NT_STATUS_OK;
+}
+
+NTSTATUS samba_path_matching_mswild_create(TALLOC_CTX *mem_ctx,
+                                          bool case_sensitive,
+                                          const char *namelist_in,
+                                          struct samba_path_matching **ppm)
+{
+       NTSTATUS status;
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct samba_path_matching *pm = NULL;
+       size_t i;
+
+       *ppm = NULL;
+
+       status = samba_path_matching_split(mem_ctx, namelist_in, &pm);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return status;
+       }
+       talloc_reparent(mem_ctx, frame, pm);
+
+       for (i = 0; i < pm->num_entries; i++) {
+               struct samba_path_matching_entry *e = &pm->entries[i];
+
+               e->is_wild = ms_has_wild(e->name);
+       }
+
+       pm->case_sensitive = case_sensitive;
+       pm->matching_fn = samba_path_create_mswild_fn;
+       *ppm = talloc_move(mem_ctx, &pm);
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
+};
+
+NTSTATUS samba_path_matching_check_last_component(struct samba_path_matching *pm,
+                                                 const char *name,
+                                                 ssize_t *p_match_idx,
+                                                 ssize_t *p_replace_start,
+                                                 ssize_t *p_replace_end)
+{
+       struct samba_path_matching_result result = {
+               .match = false,
+               .replace_start = -1,
+               .replace_end = -1,
+       };
+       ssize_t match_idx = -1;
+       NTSTATUS status = NT_STATUS_OK;
+       const char *last_component = NULL;
+       size_t i;
+
+       if (pm->num_entries == 0) {
+               goto finish;
+       }
+
+       /* Get the last component of the unix name. */
+       last_component = strrchr_m(name, '/');
+       if (last_component == NULL) {
+               last_component = name;
+       } else {
+               last_component++; /* Go past '/' */
+       }
+
+       for (i = 0; i < pm->num_entries; i++) {
+               struct samba_path_matching_entry *e = &pm->entries[i];
+
+               status = pm->matching_fn(pm, e, last_component, &result);
+               if (!NT_STATUS_IS_OK(status)) {
+                       result = (struct samba_path_matching_result) {
+                               .match = false,
+                               .replace_start = -1,
+                               .replace_end = -1,
+                       };
+                       goto finish;
+               }
+
+               if (result.match) {
+                       match_idx = i;
+                       goto finish;
+               }
+       }
+
+finish:
+       *p_match_idx = match_idx;
+       if (p_replace_start != NULL) {
+               size_t last_ofs = 0;
+
+               if (result.replace_start >= 0) {
+                       last_ofs = PTR_DIFF(last_component, name);
+               }
+
+               *p_replace_start = last_ofs + result.replace_start;
+       }
+       if (p_replace_end != NULL) {
+               size_t last_ofs = 0;
+
+               if (result.replace_end >= 0) {
+                       last_ofs = PTR_DIFF(last_component, name);
+               }
+
+               *p_replace_end = last_ofs + result.replace_end;
+       }
+       return status;
+}
diff --git a/source3/lib/util_matching.h b/source3/lib/util_matching.h
new file mode 100644 (file)
index 0000000..71b8600
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+   Unix SMB/CIFS implementation.
+   Samba utility functions
+   Copyright (C) Stefan Metzmacher 2021
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SAMBA_LIB_UTIL_MATCHING_H_
+#define _SAMBA_LIB_UTIL_MATCHING_H_
+
+struct samba_path_matching;
+
+NTSTATUS samba_path_matching_mswild_create(TALLOC_CTX *mem_ctx,
+                                          bool case_sensitive,
+                                          const char *namelist_in,
+                                          struct samba_path_matching **ppm);
+
+NTSTATUS samba_path_matching_check_last_component(struct samba_path_matching *pm,
+                                                 const char *name,
+                                                 ssize_t *p_match_idx,
+                                                 ssize_t *p_replace_start,
+                                                 ssize_t *p_replace_end);
+
+#endif /* _SAMBA_LIB_UTIL_MATCHING_H_ */
index 4764c64f296fc8df299d05cc6b25070e5a26cfbb..f94fa9a3dd0eac76f4ea113ae9704860ebc43691 100644 (file)
 */
 
 #include "includes.h"
+#include "lib/util_matching.h"
 #include "proto.h"
 
 bool run_str_match_mswild(int dummy)
 {
        const char *namelist = "/abc*.txt/xyz*.dat/a0123456789Z/";
        name_compare_entry *name_entries = NULL;
-       const struct test_name {
+       struct samba_path_matching *pmcs = NULL;
+       struct samba_path_matching *pmci = NULL;
+       const struct str_match_mswild_name {
                const char *name;
                ssize_t case_sensitive_idx;
                ssize_t case_insensitive_idx;
@@ -57,6 +60,7 @@ bool run_str_match_mswild(int dummy)
                .case_sensitive_idx = -1,
                .case_insensitive_idx = 2,
        }};
+       NTSTATUS status;
        size_t i;
        bool ret = true;
 
@@ -65,10 +69,26 @@ bool run_str_match_mswild(int dummy)
        set_namearray(&name_entries, namelist);
        SMB_ASSERT(name_entries != NULL);
 
+       status = samba_path_matching_mswild_create(talloc_tos(),
+                                                  true, /* case_sensitive */
+                                                  namelist,
+                                                  &pmcs);
+       SMB_ASSERT(NT_STATUS_IS_OK(status));
+       status = samba_path_matching_mswild_create(talloc_tos(),
+                                                  false, /* case_sensitive */
+                                                  namelist,
+                                                  &pmci);
+       SMB_ASSERT(NT_STATUS_IS_OK(status));
+
+
        for (i = 0; i < ARRAY_SIZE(names); i++) {
-               const struct test_name *n = &names[i];
+               const struct str_match_mswild_name *n = &names[i];
                bool case_sensitive_match;
                bool case_insensitive_match;
+               ssize_t cs_match_idx = -1;
+               ssize_t ci_match_idx = -1;
+               ssize_t replace_start = -1;
+               ssize_t replace_end = -1;
                bool ok = true;
 
                case_sensitive_match = is_in_path(n->name,
@@ -79,6 +99,17 @@ bool run_str_match_mswild(int dummy)
                } else {
                        ok &= !case_sensitive_match;
                }
+               status = samba_path_matching_check_last_component(pmcs,
+                                                                 n->name,
+                                                                 &cs_match_idx,
+                                                                 &replace_start,
+                                                                 &replace_end);
+               SMB_ASSERT(NT_STATUS_IS_OK(status));
+               SMB_ASSERT(replace_start == -1);
+               SMB_ASSERT(replace_end == -1);
+               if (n->case_sensitive_idx != cs_match_idx) {
+                       ok = false;
+               }
                case_insensitive_match = is_in_path(n->name,
                                                    name_entries,
                                                    false);
@@ -87,16 +118,29 @@ bool run_str_match_mswild(int dummy)
                } else {
                        ok &= !case_insensitive_match;
                }
+               status = samba_path_matching_check_last_component(pmci,
+                                                                 n->name,
+                                                                 &ci_match_idx,
+                                                                 &replace_start,
+                                                                 &replace_end);
+               SMB_ASSERT(NT_STATUS_IS_OK(status));
+               SMB_ASSERT(replace_start == -1);
+               SMB_ASSERT(replace_end == -1);
+               if (n->case_insensitive_idx != ci_match_idx) {
+                       ok = false;
+               }
 
                d_fprintf(stderr, "name[%s] "
-                         "case_sensitive[TIDX=%zd;MATCH=%u] "
-                         "case_insensitive[TIDX=%zd;MATCH=%u] "
+                         "case_sensitive[TIDX=%zd;MATCH=%u;MIDX=%zd] "
+                         "case_insensitive[TIDX=%zd;MATCH=%u;MIDX=%zd] "
                          "%s\n",
                          n->name,
                          n->case_sensitive_idx,
                          case_sensitive_match,
+                         cs_match_idx,
                          n->case_insensitive_idx,
                          case_insensitive_match,
+                         ci_match_idx,
                          ok ? "OK" : "FAIL");
 
                ret &= ok;
index 6130d1e3e9959d6b3ad40403d58b85ac82ebbe3b..d231584a8506de1dce6a1d07535852bfb0317590 100644 (file)
@@ -287,6 +287,7 @@ bld.SAMBA3_SUBSYSTEM('samba3util',
                           lib/util_file.c
                           lib/util.c
                           lib/util_path.c
+                          lib/util_matching.c
                           lib/util_procid.c
                           lib/util_sock.c
                           lib/util_tsock.c