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 1032968 : static int null_match(const char *p)
36 : {
37 1033799 : for (;*p;p++) {
38 3751 : if (*p != '*' &&
39 3578 : *p != '<' &&
40 3366 : *p != '"' &&
41 3106 : *p != '>') return -1;
42 : }
43 1029818 : 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 11210216 : 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 11394815 : while ((c = next_codepoint(p, &size))) {
71 2078792 : p += size;
72 :
73 2078792 : switch (c) {
74 1112583 : case '*':
75 : /* a '*' matches zero or more characters of any type */
76 1112583 : if (max_n != NULL && max_n->predot &&
77 293 : max_n->predot <= n) {
78 293 : return null_match(p);
79 : }
80 11072959 : for (i=0; n[i]; i += size_n) {
81 10041032 : next_codepoint(n+i, &size_n);
82 10041032 : if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) {
83 80356 : return 0;
84 : }
85 : }
86 1031927 : if (max_n != NULL && (!max_n->predot ||
87 0 : max_n->predot > n)) {
88 1031927 : max_n->predot = n;
89 : }
90 1031927 : return null_match(p);
91 :
92 998 : case '<':
93 : /* a '<' matches zero or more characters of
94 : any type, but stops matching at the last
95 : '.' in the string. */
96 998 : if (max_n != NULL && max_n->predot &&
97 261 : max_n->predot <= n) {
98 261 : return null_match(p);
99 : }
100 737 : if (max_n != NULL && max_n->postdot &&
101 146 : max_n->postdot <= n && n <= ldot) {
102 93 : return -1;
103 : }
104 3791 : for (i=0; n[i]; i += size_n) {
105 3533 : next_codepoint(n+i, &size_n);
106 3533 : if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) return 0;
107 3455 : if (n+i == ldot) {
108 308 : if (ms_fnmatch_core(p, n+i+size_n, max_n+1, ldot, is_case_sensitive) == 0) return 0;
109 302 : if (max_n != NULL) {
110 302 : if (!max_n->postdot ||
111 0 : max_n->postdot > n) {
112 302 : max_n->postdot = n;
113 : }
114 : }
115 302 : return -1;
116 : }
117 : }
118 258 : if (max_n != NULL && (!max_n->predot ||
119 0 : max_n->predot > n)) {
120 258 : max_n->predot = n;
121 : }
122 258 : return null_match(p);
123 :
124 696 : case '?':
125 : /* a '?' matches any single character */
126 696 : if (! *n) {
127 68 : return -1;
128 : }
129 627 : next_codepoint(n, &size_n);
130 627 : n += size_n;
131 627 : break;
132 :
133 691 : case '>':
134 : /* a '?' matches any single character, but
135 : treats '.' specially */
136 691 : if (n[0] == '.') {
137 136 : if (! n[1] && null_match(p) == 0) {
138 16 : return 0;
139 : }
140 120 : break;
141 : }
142 556 : if (! *n) return null_match(p);
143 489 : next_codepoint(n, &size_n);
144 489 : n += size_n;
145 489 : break;
146 :
147 790 : case '"':
148 : /* a bit like a soft '.' */
149 790 : if (*n == 0 && null_match(p) == 0) {
150 8 : return 0;
151 : }
152 782 : if (*n != '.') return -1;
153 122 : next_codepoint(n, &size_n);
154 122 : n += size_n;
155 122 : break;
156 :
157 963034 : default:
158 963034 : c2 = next_codepoint(n, &size_n);
159 963034 : if (c != c2) {
160 779891 : if (is_case_sensitive) {
161 1380 : return -1;
162 : }
163 778476 : if (codepoint_cmpi(c, c2) != 0) {
164 778338 : return -1;
165 : }
166 : }
167 183241 : n += size_n;
168 183241 : break;
169 : }
170 : }
171 :
172 9316023 : if (! *n) {
173 156 : return 0;
174 : }
175 :
176 9314912 : return -1;
177 : }
178 :
179 16423126 : int ms_fnmatch_protocol(const char *pattern, const char *string, int protocol,
180 : bool is_case_sensitive)
181 : {
182 16423126 : int ret = -1;
183 800193 : size_t count, i;
184 :
185 16423126 : if (strcmp(string, "..") == 0) {
186 3361 : string = ".";
187 : }
188 :
189 16423126 : if (strpbrk(pattern, "<>*?\"") == NULL) {
190 : /* this is not just an optimisation - it is essential
191 : for LANMAN1 correctness */
192 15257498 : return strcasecmp_m(pattern, string);
193 : }
194 :
195 1165628 : 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 5082639 : for (count=i=0;pattern[i];i++) {
225 3917296 : if (pattern[i] == '*' || pattern[i] == '<') count++;
226 : }
227 :
228 : /* If the pattern includes '*' or '<' */
229 1165343 : if (count >= 1) {
230 1165148 : struct max_n max_n[count];
231 :
232 1165148 : memset(max_n, 0, sizeof(struct max_n) * count);
233 :
234 1165148 : ret = ms_fnmatch_core(pattern, string, max_n, strrchr(string, '.'),
235 : is_case_sensitive);
236 : } else {
237 195 : ret = ms_fnmatch_core(pattern, string, NULL, strrchr(string, '.'),
238 : is_case_sensitive);
239 : }
240 :
241 1165058 : return ret;
242 : }
243 :
244 :
245 : /** a generic fnmatch function - uses for non-CIFS pattern matching */
246 15378857 : int gen_fnmatch(const char *pattern, const char *string)
247 : {
248 15378857 : return ms_fnmatch_protocol(pattern, string, PROTOCOL_NT1, false);
249 : }
|