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) resemblence to that
23 : code now
24 : */
25 :
26 : /**
27 : * @file
28 : * @brief MS-style Filename matching
29 : */
30 :
31 : #include "includes.h"
32 : #include "libcli/smb/smb_constants.h"
33 :
34 640687 : static int null_match(const char *p)
35 : {
36 641398 : for (;*p;p++) {
37 7534 : if (*p != '*' &&
38 7323 : *p != '<' &&
39 7007 : *p != '"' &&
40 6506 : *p != '>') return -1;
41 : }
42 637579 : return 0;
43 : }
44 :
45 : /*
46 : the max_n structure is purely for efficiency, it doesn't contribute
47 : to the matching algorithm except by ensuring that the algorithm does
48 : not grow exponentially
49 : */
50 : struct max_n {
51 : const char *predot;
52 : const char *postdot;
53 : };
54 :
55 :
56 : /*
57 : p and n are the pattern and string being matched. The max_n array is
58 : an optimisation only. The ldot pointer is NULL if the string does
59 : not contain a '.', otherwise it points at the last dot in 'n'.
60 : */
61 7621519 : static int ms_fnmatch_core(const char *p, const char *n,
62 : struct max_n *max_n, const char *ldot,
63 : bool is_case_sensitive)
64 : {
65 : codepoint_t c, c2;
66 : int i;
67 : size_t size, size_n;
68 :
69 14218141 : while ((c = next_codepoint(p, &size))) {
70 1687238 : p += size;
71 :
72 1687238 : switch (c) {
73 720907 : case '*':
74 : /* a '*' matches zero or more characters of any type */
75 721028 : if (max_n != NULL && max_n->predot &&
76 121 : max_n->predot <= n) {
77 121 : return null_match(p);
78 : }
79 7489622 : for (i=0; n[i]; i += size_n) {
80 6849554 : next_codepoint(n+i, &size_n);
81 6849554 : if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) {
82 80558 : return 0;
83 : }
84 : }
85 640218 : if (max_n != NULL && (!max_n->predot ||
86 0 : max_n->predot > n)) {
87 640218 : max_n->predot = n;
88 : }
89 640078 : return null_match(p);
90 :
91 451 : case '<':
92 : /* a '<' matches zero or more characters of
93 : any type, but stops matching at the last
94 : '.' in the string. */
95 545 : if (max_n != NULL && max_n->predot &&
96 94 : max_n->predot <= n) {
97 94 : return null_match(p);
98 : }
99 377 : if (max_n != NULL && max_n->postdot &&
100 40 : max_n->postdot <= n && n <= ldot) {
101 18 : return -1;
102 : }
103 2503 : for (i=0; n[i]; i += size_n) {
104 2395 : next_codepoint(n+i, &size_n);
105 2395 : if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) return 0;
106 2323 : if (n+i == ldot) {
107 147 : if (ms_fnmatch_core(p, n+i+size_n, max_n+1, ldot, is_case_sensitive) == 0) return 0;
108 129 : if (max_n != NULL) {
109 129 : if (!max_n->postdot ||
110 0 : max_n->postdot > n) {
111 129 : max_n->postdot = n;
112 : }
113 : }
114 129 : return -1;
115 : }
116 : }
117 120 : if (max_n != NULL && (!max_n->predot ||
118 0 : max_n->predot > n)) {
119 120 : max_n->predot = n;
120 : }
121 120 : return null_match(p);
122 :
123 716 : case '?':
124 : /* a '?' matches any single character */
125 716 : if (! *n) {
126 71 : return -1;
127 : }
128 644 : next_codepoint(n, &size_n);
129 644 : n += size_n;
130 644 : break;
131 :
132 586 : case '>':
133 : /* a '?' matches any single character, but
134 : treats '.' specially */
135 586 : if (n[0] == '.') {
136 111 : if (! n[1] && null_match(p) == 0) {
137 8 : return 0;
138 : }
139 103 : break;
140 : }
141 475 : if (! *n) return null_match(p);
142 395 : next_codepoint(n, &size_n);
143 395 : n += size_n;
144 395 : break;
145 :
146 1064 : case '"':
147 : /* a bit like a soft '.' */
148 1064 : if (*n == 0 && null_match(p) == 0) {
149 14 : return 0;
150 : }
151 1050 : if (*n != '.') return -1;
152 213 : next_codepoint(n, &size_n);
153 213 : n += size_n;
154 213 : break;
155 :
156 963514 : default:
157 963514 : c2 = next_codepoint(n, &size_n);
158 963514 : if (c != c2) {
159 780678 : if (is_case_sensitive) {
160 1380 : return -1;
161 : }
162 779263 : if (codepoint_cmpi(c, c2) != 0) {
163 779132 : return -1;
164 : }
165 : }
166 182903 : n += size_n;
167 182903 : break;
168 : }
169 : }
170 :
171 6118539 : if (! *n) {
172 170 : return 0;
173 : }
174 :
175 6118351 : return -1;
176 : }
177 :
178 14411123 : int ms_fnmatch_protocol(const char *pattern, const char *string, int protocol,
179 : bool is_case_sensitive)
180 : {
181 14411123 : int ret = -1;
182 : size_t count, i;
183 :
184 14411123 : if (strcmp(string, "..") == 0) {
185 5452 : string = ".";
186 : }
187 :
188 14411123 : if (strpbrk(pattern, "<>*?\"") == NULL) {
189 : /* this is not just an optimisation - it is essential
190 : for LANMAN1 correctness */
191 13641387 : return strcasecmp_m(pattern, string);
192 : }
193 :
194 769736 : if (protocol <= PROTOCOL_LANMAN2) {
195 313 : char *p = talloc_strdup(NULL, pattern);
196 313 : if (p == NULL) {
197 0 : return -1;
198 : }
199 : /*
200 : for older negotiated protocols it is possible to
201 : translate the pattern to produce a "new style"
202 : pattern that exactly matches w2k behaviour
203 : */
204 4417 : for (i=0;p[i];i++) {
205 4157 : if (p[i] == '?') {
206 13 : p[i] = '>';
207 4521 : } else if (p[i] == '.' &&
208 711 : (p[i+1] == '?' ||
209 684 : p[i+1] == '*' ||
210 300 : p[i+1] == 0)) {
211 34 : p[i] = '"';
212 4441 : } else if (p[i] == '*' &&
213 352 : p[i+1] == '.') {
214 256 : p[i] = '<';
215 : }
216 : }
217 313 : ret = ms_fnmatch_protocol(p, string, PROTOCOL_NT1,
218 : is_case_sensitive);
219 313 : talloc_free(p);
220 313 : return ret;
221 : }
222 :
223 4213610 : for (count=i=0;pattern[i];i++) {
224 3444386 : if (pattern[i] == '*' || pattern[i] == '<') count++;
225 : }
226 :
227 : /* If the pattern includes '*' or '<' */
228 769423 : if (count >= 1) {
229 769202 : struct max_n max_n[count];
230 :
231 769395 : memset(max_n, 0, sizeof(struct max_n) * count);
232 :
233 769202 : ret = ms_fnmatch_core(pattern, string, max_n, strrchr(string, '.'),
234 : is_case_sensitive);
235 : } else {
236 221 : ret = ms_fnmatch_core(pattern, string, NULL, strrchr(string, '.'),
237 : is_case_sensitive);
238 : }
239 :
240 769224 : return ret;
241 : }
242 :
243 :
244 : /** a generic fnmatch function - uses for non-CIFS pattern matching */
245 13750199 : int gen_fnmatch(const char *pattern, const char *string)
246 : {
247 13750199 : return ms_fnmatch_protocol(pattern, string, PROTOCOL_NT1, false);
248 : }
|