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 "replace.h"
32 : #include "lib/util/samba_util.h"
33 : #include "libcli/smb/smb_constants.h"
34 :
35 140244 : static int null_match(const char *p)
36 : {
37 140871 : for (;*p;p++) {
38 1785 : if (*p != '*' &&
39 1624 : *p != '<' &&
40 1520 : *p != '"' &&
41 1351 : *p != '>') return -1;
42 : }
43 139086 : 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 1147919 : 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 : codepoint_t c, c2;
67 : int i;
68 : size_t size, size_n;
69 :
70 1265833 : while ((c = next_codepoint(p, &size))) {
71 532442 : p += size;
72 :
73 532442 : switch (c) {
74 156056 : case '*':
75 : /* a '*' matches zero or more characters of any type */
76 156056 : if (max_n != NULL && max_n->predot &&
77 359 : max_n->predot <= n) {
78 359 : return null_match(p);
79 : }
80 1018472 : for (i=0; n[i]; i += size_n) {
81 879057 : next_codepoint(n+i, &size_n);
82 879057 : if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) {
83 16282 : return 0;
84 : }
85 : }
86 139415 : if (max_n != NULL && (!max_n->predot ||
87 0 : max_n->predot > n)) {
88 139415 : max_n->predot = n;
89 : }
90 139415 : return null_match(p);
91 :
92 426 : case '<':
93 : /* a '<' matches zero or more characters of
94 : any type, but stops matching at the last
95 : '.' in the string. */
96 426 : if (max_n != NULL && max_n->predot &&
97 87 : max_n->predot <= n) {
98 87 : return null_match(p);
99 : }
100 339 : if (max_n != NULL && max_n->postdot &&
101 28 : max_n->postdot <= n && n <= ldot) {
102 18 : return -1;
103 : }
104 1219 : for (i=0; n[i]; i += size_n) {
105 1066 : next_codepoint(n+i, &size_n);
106 1066 : if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) return 0;
107 1050 : if (n+i == ldot) {
108 152 : if (ms_fnmatch_core(p, n+i+size_n, max_n+1, ldot, is_case_sensitive) == 0) return 0;
109 148 : if (max_n != NULL) {
110 148 : if (!max_n->postdot ||
111 0 : max_n->postdot > n) {
112 148 : max_n->postdot = n;
113 : }
114 : }
115 148 : return -1;
116 : }
117 : }
118 153 : if (max_n != NULL && (!max_n->predot ||
119 0 : max_n->predot > n)) {
120 153 : max_n->predot = n;
121 : }
122 153 : return null_match(p);
123 :
124 423 : case '?':
125 : /* a '?' matches any single character */
126 423 : if (! *n) {
127 44 : return -1;
128 : }
129 379 : next_codepoint(n, &size_n);
130 379 : n += size_n;
131 379 : break;
132 :
133 490 : case '>':
134 : /* a '?' matches any single character, but
135 : treats '.' specially */
136 490 : if (n[0] == '.') {
137 140 : if (! n[1] && null_match(p) == 0) {
138 4 : return 0;
139 : }
140 136 : break;
141 : }
142 350 : if (! *n) return null_match(p);
143 294 : next_codepoint(n, &size_n);
144 294 : n += size_n;
145 294 : break;
146 :
147 585 : case '"':
148 : /* a bit like a soft '.' */
149 585 : if (*n == 0 && null_match(p) == 0) {
150 4 : return 0;
151 : }
152 581 : if (*n != '.') return -1;
153 143 : next_codepoint(n, &size_n);
154 143 : n += size_n;
155 143 : break;
156 :
157 374462 : default:
158 374462 : c2 = next_codepoint(n, &size_n);
159 374462 : if (c != c2) {
160 257516 : if (is_case_sensitive) {
161 0 : return -1;
162 : }
163 257516 : if (codepoint_cmpi(c, c2) != 0) {
164 257500 : return -1;
165 : }
166 : }
167 116962 : n += size_n;
168 116962 : break;
169 : }
170 : }
171 :
172 733391 : if (! *n) {
173 87 : return 0;
174 : }
175 :
176 733304 : return -1;
177 : }
178 :
179 7394049 : int ms_fnmatch_protocol(const char *pattern, const char *string, int protocol,
180 : bool is_case_sensitive)
181 : {
182 7394049 : int ret = -1;
183 : size_t count, i;
184 :
185 7394049 : if (strcmp(string, "..") == 0) {
186 3387 : string = ".";
187 : }
188 :
189 7394049 : if (strpbrk(pattern, "<>*?\"") == NULL) {
190 : /* this is not just an optimisation - it is essential
191 : for LANMAN1 correctness */
192 7126405 : return strcasecmp_m(pattern, string);
193 : }
194 :
195 267644 : if (protocol <= PROTOCOL_LANMAN2) {
196 0 : char *p = talloc_strdup(NULL, pattern);
197 0 : 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 0 : for (i=0;p[i];i++) {
206 0 : if (p[i] == '?') {
207 0 : p[i] = '>';
208 0 : } else if (p[i] == '.' &&
209 0 : (p[i+1] == '?' ||
210 0 : p[i+1] == '*' ||
211 0 : p[i+1] == 0)) {
212 0 : p[i] = '"';
213 0 : } else if (p[i] == '*' &&
214 0 : p[i+1] == '.') {
215 0 : p[i] = '<';
216 : }
217 : }
218 0 : ret = ms_fnmatch_protocol(p, string, PROTOCOL_NT1,
219 : is_case_sensitive);
220 0 : talloc_free(p);
221 0 : return ret;
222 : }
223 :
224 3114626 : for (count=i=0;pattern[i];i++) {
225 2846982 : if (pattern[i] == '*' || pattern[i] == '<') count++;
226 : }
227 :
228 : /* If the pattern includes '*' or '<' */
229 267644 : if (count >= 1) {
230 267483 : struct max_n max_n[count];
231 :
232 267483 : memset(max_n, 0, sizeof(struct max_n) * count);
233 :
234 267483 : ret = ms_fnmatch_core(pattern, string, max_n, strrchr(string, '.'),
235 : is_case_sensitive);
236 : } else {
237 161 : ret = ms_fnmatch_core(pattern, string, NULL, strrchr(string, '.'),
238 : is_case_sensitive);
239 : }
240 :
241 267644 : return ret;
242 : }
243 :
244 :
245 : /** a generic fnmatch function - uses for non-CIFS pattern matching */
246 7251610 : int gen_fnmatch(const char *pattern, const char *string)
247 : {
248 7251610 : return ms_fnmatch_protocol(pattern, string, PROTOCOL_NT1, false);
249 : }
|