Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : filename matching routine
4 : Copyright (C) Andrew Tridgell 1992-2004
5 :
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.
10 :
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.
15 :
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/>.
18 : */
19 :
20 : /*
21 : This module was originally based on fnmatch.c copyright by the Free
22 : Software Foundation. It bears little (if any) resemblance to that
23 : code now
24 : */
25 :
26 : /**
27 : * @file
28 : * @brief MS-style Filename matching
29 : */
30 :
31 : #include "replace.h"
32 : #include "lib/util/samba_util.h"
33 : #include "libcli/smb/smb_constants.h"
34 :
35 1030747 : static int null_match(const char *p)
36 : {
37 1031404 : for (;*p;p++) {
38 3060 : if (*p != '*' &&
39 2905 : *p != '<' &&
40 2809 : *p != '"' &&
41 2574 : *p != '>') return -1;
42 : }
43 1028114 : return 0;
44 : }
45 :
46 : /*
47 : the max_n structure is purely for efficiency, it doesn't contribute
48 : to the matching algorithm except by ensuring that the algorithm does
49 : not grow exponentially
50 : */
51 : struct max_n {
52 : const char *predot;
53 : const char *postdot;
54 : };
55 :
56 :
57 : /*
58 : p and n are the pattern and string being matched. The max_n array is
59 : an optimisation only. The ldot pointer is NULL if the string does
60 : not contain a '.', otherwise it points at the last dot in 'n'.
61 : */
62 11191213 : static int ms_fnmatch_core(const char *p, const char *n,
63 : struct max_n *max_n, const char *ldot,
64 : bool is_case_sensitive)
65 : {
66 1295 : codepoint_t c, c2;
67 1295 : int i;
68 1295 : size_t size, size_n;
69 :
70 11375527 : while ((c = next_codepoint(p, &size))) {
71 2074635 : p += size;
72 :
73 2074635 : switch (c) {
74 1110652 : case '*':
75 : /* a '*' matches zero or more characters of any type */
76 1110652 : if (max_n != NULL && max_n->predot &&
77 151 : max_n->predot <= n) {
78 151 : return null_match(p);
79 : }
80 11055309 : for (i=0; n[i]; i += size_n) {
81 10025185 : next_codepoint(n+i, &size_n);
82 10025185 : if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) {
83 80370 : return 0;
84 : }
85 : }
86 1030124 : if (max_n != NULL && (!max_n->predot ||
87 0 : max_n->predot > n)) {
88 1030124 : max_n->predot = n;
89 : }
90 1030124 : return null_match(p);
91 :
92 494 : case '<':
93 : /* a '<' matches zero or more characters of
94 : any type, but stops matching at the last
95 : '.' in the string. */
96 494 : if (max_n != NULL && max_n->predot &&
97 93 : max_n->predot <= n) {
98 93 : return null_match(p);
99 : }
100 401 : if (max_n != NULL && max_n->postdot &&
101 34 : max_n->postdot <= n && n <= ldot) {
102 28 : return -1;
103 : }
104 2612 : for (i=0; n[i]; i += size_n) {
105 2483 : next_codepoint(n+i, &size_n);
106 2483 : if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) return 0;
107 2399 : if (n+i == ldot) {
108 160 : if (ms_fnmatch_core(p, n+i+size_n, max_n+1, ldot, is_case_sensitive) == 0) return 0;
109 152 : if (max_n != NULL) {
110 152 : if (!max_n->postdot ||
111 0 : max_n->postdot > n) {
112 152 : max_n->postdot = n;
113 : }
114 : }
115 152 : return -1;
116 : }
117 : }
118 129 : if (max_n != NULL && (!max_n->predot ||
119 0 : max_n->predot > n)) {
120 129 : max_n->predot = n;
121 : }
122 129 : return null_match(p);
123 :
124 506 : case '?':
125 : /* a '?' matches any single character */
126 506 : if (! *n) {
127 42 : return -1;
128 : }
129 463 : next_codepoint(n, &size_n);
130 463 : n += size_n;
131 463 : break;
132 :
133 544 : case '>':
134 : /* a '?' matches any single character, but
135 : treats '.' specially */
136 544 : if (n[0] == '.') {
137 141 : if (! n[1] && null_match(p) == 0) {
138 8 : return 0;
139 : }
140 133 : break;
141 : }
142 404 : if (! *n) return null_match(p);
143 328 : next_codepoint(n, &size_n);
144 328 : n += size_n;
145 328 : break;
146 :
147 850 : case '"':
148 : /* a bit like a soft '.' */
149 850 : if (*n == 0 && null_match(p) == 0) {
150 6 : return 0;
151 : }
152 844 : if (*n != '.') return -1;
153 183 : next_codepoint(n, &size_n);
154 183 : n += size_n;
155 183 : break;
156 :
157 961589 : default:
158 961589 : c2 = next_codepoint(n, &size_n);
159 961589 : if (c != c2) {
160 778468 : if (is_case_sensitive) {
161 1380 : return -1;
162 : }
163 777053 : if (codepoint_cmpi(c, c2) != 0) {
164 776927 : return -1;
165 : }
166 : }
167 183207 : n += size_n;
168 183207 : break;
169 : }
170 : }
171 :
172 9300892 : if (! *n) {
173 162 : return 0;
174 : }
175 :
176 9299775 : return -1;
177 : }
178 :
179 16425239 : int ms_fnmatch_protocol(const char *pattern, const char *string, int protocol,
180 : bool is_case_sensitive)
181 : {
182 16425239 : int ret = -1;
183 800193 : size_t count, i;
184 :
185 16425239 : if (strcmp(string, "..") == 0) {
186 3362 : string = ".";
187 : }
188 :
189 16425239 : if (strpbrk(pattern, "<>*?\"") == NULL) {
190 : /* this is not just an optimisation - it is essential
191 : for LANMAN1 correctness */
192 15261569 : return strcasecmp_m(pattern, string);
193 : }
194 :
195 1163670 : if (protocol <= PROTOCOL_LANMAN2) {
196 285 : char *p = talloc_strdup(NULL, pattern);
197 285 : if (p == NULL) {
198 0 : return -1;
199 : }
200 : /*
201 : for older negotiated protocols it is possible to
202 : translate the pattern to produce a "new style"
203 : pattern that exactly matches w2k behaviour
204 : */
205 4386 : for (i=0;p[i];i++) {
206 4101 : if (p[i] == '?') {
207 13 : p[i] = '>';
208 4088 : } else if (p[i] == '.' &&
209 370 : (p[i+1] == '?' ||
210 370 : p[i+1] == '*' ||
211 300 : p[i+1] == 0)) {
212 20 : p[i] = '"';
213 4068 : } else if (p[i] == '*' &&
214 310 : p[i+1] == '.') {
215 242 : p[i] = '<';
216 : }
217 : }
218 285 : ret = ms_fnmatch_protocol(p, string, PROTOCOL_NT1,
219 : is_case_sensitive);
220 285 : talloc_free(p);
221 285 : return ret;
222 : }
223 :
224 5077321 : for (count=i=0;pattern[i];i++) {
225 3913936 : if (pattern[i] == '*' || pattern[i] == '<') count++;
226 : }
227 :
228 : /* If the pattern includes '*' or '<' */
229 1163385 : if (count >= 1) {
230 1163182 : struct max_n max_n[count];
231 :
232 1163182 : memset(max_n, 0, sizeof(struct max_n) * count);
233 :
234 1163182 : ret = ms_fnmatch_core(pattern, string, max_n, strrchr(string, '.'),
235 : is_case_sensitive);
236 : } else {
237 203 : ret = ms_fnmatch_core(pattern, string, NULL, strrchr(string, '.'),
238 : is_case_sensitive);
239 : }
240 :
241 1163100 : return ret;
242 : }
243 :
244 :
245 : /** a generic fnmatch function - uses for non-CIFS pattern matching */
246 15383165 : int gen_fnmatch(const char *pattern, const char *string)
247 : {
248 15383165 : return ms_fnmatch_protocol(pattern, string, PROTOCOL_NT1, false);
249 : }
|