2 Unix SMB/CIFS implementation.
3 Samba utility functions
4 Copyright (C) Stefan Metzmacher 2021
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "lib/util_matching.h"
22 #include "lib/util/string_wrappers.h"
24 struct samba_path_matching_entry
{
30 struct samba_path_matching_result
{
31 ssize_t replace_start
;
36 struct samba_path_matching
{
38 NTSTATUS (*matching_fn
)(const struct samba_path_matching
*pm
,
39 const struct samba_path_matching_entry
*e
,
40 const char *namecomponent
,
41 struct samba_path_matching_result
*result
);
43 struct samba_path_matching_entry
*entries
;
46 static NTSTATUS
samba_path_matching_split(TALLOC_CTX
*mem_ctx
,
47 const char *namelist_in
,
48 struct samba_path_matching
**ppm
)
50 TALLOC_CTX
*frame
= talloc_stackframe();
51 char *name_end
= NULL
;
52 char *namelist
= NULL
;
53 char *namelist_end
= NULL
;
55 struct samba_path_matching
*pm
= NULL
;
56 size_t num_entries
= 0;
57 struct samba_path_matching_entry
*entries
= NULL
;
61 pm
= talloc_zero(mem_ctx
, struct samba_path_matching
);
64 return NT_STATUS_NO_MEMORY
;
66 talloc_reparent(mem_ctx
, frame
, pm
);
68 namelist
= talloc_strdup(frame
, namelist_in
);
69 if (namelist
== NULL
) {
71 return NT_STATUS_NO_MEMORY
;
75 namelist_end
= &namelist
[strlen(namelist
)];
78 * We need to make two passes over the string. The
79 * first to count the number of elements, the second
82 * The 1st time entries is NULL.
83 * the 2nd time entries is allocated.
86 while (nameptr
<= namelist_end
) {
88 if (*nameptr
== '\0') {
92 if (*nameptr
== '/') {
93 /* cope with multiple (useless) /s) */
98 /* find the next '/' or consume remaining */
99 name_end
= strchr_m(nameptr
, '/');
100 if (entries
!= NULL
) {
101 if (name_end
!= NULL
) {
104 entries
[num_entries
].name
= talloc_strdup(entries
,
106 if (entries
[num_entries
].name
== NULL
) {
108 return NT_STATUS_NO_MEMORY
;
112 if (name_end
!= NULL
) {
113 /* next segment please */
114 nameptr
= name_end
+ 1;
118 /* no entries remaining */
122 if (num_entries
== 0) {
124 * No entries in the first round => we're done
129 if (entries
!= NULL
) {
131 * We finished the 2nd round => we're done
137 * Now allocate the array and loop again
138 * in order to split the names.
140 entries
= talloc_zero_array(pm
,
141 struct samba_path_matching_entry
,
143 if (entries
== NULL
) {
145 return NT_STATUS_NO_MEMORY
;
152 pm
->num_entries
= num_entries
;
153 pm
->entries
= entries
;
154 *ppm
= talloc_move(mem_ctx
, &pm
);
159 static NTSTATUS
samba_path_create_mswild_fn(const struct samba_path_matching
*pm
,
160 const struct samba_path_matching_entry
*e
,
161 const char *namecomponent
,
162 struct samba_path_matching_result
*result
)
167 match
= mask_match(namecomponent
, e
->name
, pm
->case_sensitive
);
168 } else if (pm
->case_sensitive
) {
169 match
= (strcmp(namecomponent
, e
->name
) == 0);
171 match
= (strcasecmp_m(namecomponent
, e
->name
) == 0);
174 *result
= (struct samba_path_matching_result
) {
183 NTSTATUS
samba_path_matching_mswild_create(TALLOC_CTX
*mem_ctx
,
185 const char *namelist_in
,
186 struct samba_path_matching
**ppm
)
189 TALLOC_CTX
*frame
= talloc_stackframe();
190 struct samba_path_matching
*pm
= NULL
;
195 status
= samba_path_matching_split(mem_ctx
, namelist_in
, &pm
);
196 if (!NT_STATUS_IS_OK(status
)) {
200 talloc_reparent(mem_ctx
, frame
, pm
);
202 for (i
= 0; i
< pm
->num_entries
; i
++) {
203 struct samba_path_matching_entry
*e
= &pm
->entries
[i
];
205 e
->is_wild
= ms_has_wild(e
->name
);
208 pm
->case_sensitive
= case_sensitive
;
209 pm
->matching_fn
= samba_path_create_mswild_fn
;
210 *ppm
= talloc_move(mem_ctx
, &pm
);
215 static int samba_path_matching_regex_sub1_destructor(struct samba_path_matching
*pm
)
219 for (i
= 0; i
< pm
->num_entries
; i
++) {
220 struct samba_path_matching_entry
*e
= &pm
->entries
[i
];
230 static NTSTATUS
samba_path_create_regex_sub1_fn(const struct samba_path_matching
*pm
,
231 const struct samba_path_matching_entry
*e
,
232 const char *namecomponent
,
233 struct samba_path_matching_result
*result
)
235 if (e
->re
.re_nsub
== 1) {
236 regmatch_t matches
[2] = { };
239 ret
= regexec(&e
->re
, namecomponent
, 2, matches
, 0);
241 *result
= (struct samba_path_matching_result
) {
243 .replace_start
= matches
[1].rm_so
,
244 .replace_end
= matches
[1].rm_eo
,
251 *result
= (struct samba_path_matching_result
) {
260 NTSTATUS
samba_path_matching_regex_sub1_create(TALLOC_CTX
*mem_ctx
,
261 const char *namelist_in
,
262 struct samba_path_matching
**ppm
)
265 TALLOC_CTX
*frame
= talloc_stackframe();
266 struct samba_path_matching
*pm
= NULL
;
271 status
= samba_path_matching_split(mem_ctx
, namelist_in
, &pm
);
272 if (!NT_STATUS_IS_OK(status
)) {
276 talloc_reparent(mem_ctx
, frame
, pm
);
278 for (i
= 0; i
< pm
->num_entries
; i
++) {
279 struct samba_path_matching_entry
*e
= &pm
->entries
[i
];
282 ret
= regcomp(&e
->re
, e
->name
, 0);
286 regerror(ret
, &e
->re
, buf
, sizeof(buf
));
288 DBG_ERR("idx[%zu] regcomp: /%s/ - %d - %s\n",
289 i
, e
->name
, ret
, buf
);
291 status
= NT_STATUS_INVALID_PARAMETER
;
296 if (e
->re
.re_nsub
!= 1) {
297 DBG_ERR("idx[%zu] regcomp: /%s/ - re_nsub[%zu] != 1\n",
298 i
, e
->name
, e
->re
.re_nsub
);
299 status
= NT_STATUS_INVALID_PARAMETER
;
304 talloc_set_destructor(pm
, samba_path_matching_regex_sub1_destructor
);
306 pm
->case_sensitive
= true;
307 pm
->matching_fn
= samba_path_create_regex_sub1_fn
;
308 *ppm
= talloc_move(mem_ctx
, &pm
);
313 for (; i
>= 0; i
--) {
314 struct samba_path_matching_entry
*e
= &pm
->entries
[i
];
323 NTSTATUS
samba_path_matching_check_last_component(struct samba_path_matching
*pm
,
325 ssize_t
*p_match_idx
,
326 ssize_t
*p_replace_start
,
327 ssize_t
*p_replace_end
)
329 struct samba_path_matching_result result
= {
334 ssize_t match_idx
= -1;
335 NTSTATUS status
= NT_STATUS_OK
;
336 const char *last_component
= NULL
;
339 if (pm
->num_entries
== 0) {
343 /* Get the last component of the unix name. */
344 last_component
= strrchr_m(name
, '/');
345 if (last_component
== NULL
) {
346 last_component
= name
;
348 last_component
++; /* Go past '/' */
351 for (i
= 0; i
< pm
->num_entries
; i
++) {
352 struct samba_path_matching_entry
*e
= &pm
->entries
[i
];
354 status
= pm
->matching_fn(pm
, e
, last_component
, &result
);
355 if (!NT_STATUS_IS_OK(status
)) {
356 result
= (struct samba_path_matching_result
) {
371 *p_match_idx
= match_idx
;
372 if (p_replace_start
!= NULL
) {
375 if (result
.replace_start
>= 0) {
376 last_ofs
= PTR_DIFF(last_component
, name
);
379 *p_replace_start
= last_ofs
+ result
.replace_start
;
381 if (p_replace_end
!= NULL
) {
384 if (result
.replace_end
>= 0) {
385 last_ofs
= PTR_DIFF(last_component
, name
);
388 *p_replace_end
= last_ofs
+ result
.replace_end
;