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 1032472 : static int null_match(const char *p)
36 : {
37 1032893 : for (;*p;p++) {
38 2855 : if (*p != '*' &&
39 2793 : *p != '<' &&
40 2633 : *p != '"' &&
41 2505 : *p != '>') return -1;
42 : }
43 1029808 : 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 11208900 : 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 11392911 : while ((c = next_codepoint(p, &size))) {
71 2076914 : p += size;
72 :
73 2076914 : switch (c) {
74 1112341 : case '*':
75 : /* a '*' matches zero or more characters of any type */
76 1112341 : if (max_n != NULL && max_n->predot &&
77 141 : max_n->predot <= n) {
78 141 : return null_match(p);
79 : }
80 11072326 : for (i=0; n[i]; i += size_n) {
81 10040491 : next_codepoint(n+i, &size_n);
82 10040491 : if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) {
83 80358 : return 0;
84 : }
85 : }
86 1031835 : if (max_n != NULL && (!max_n->predot ||
87 0 : max_n->predot > n)) {
88 1031835 : max_n->predot = n;
89 : }
90 1031835 : return null_match(p);
91 :
92 721 : case '<':
93 : /* a '<' matches zero or more characters of
94 : any type, but stops matching at the last
95 : '.' in the string. */
96 721 : if (max_n != NULL && max_n->predot &&
97 161 : max_n->predot <= n) {
98 161 : return null_match(p);
99 : }
100 560 : if (max_n != NULL && max_n->postdot &&
101 132 : max_n->postdot <= n && n <= ldot) {
102 98 : return -1;
103 : }
104 2985 : for (i=0; n[i]; i += size_n) {
105 2819 : next_codepoint(n+i, &size_n);
106 2819 : if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) return 0;
107 2743 : if (n+i == ldot) {
108 220 : if (ms_fnmatch_core(p, n+i+size_n, max_n+1, ldot, is_case_sensitive) == 0) return 0;
109 206 : if (max_n != NULL) {
110 206 : if (!max_n->postdot ||
111 0 : max_n->postdot > n) {
112 206 : max_n->postdot = n;
113 : }
114 : }
115 206 : return -1;
116 : }
117 : }
118 166 : if (max_n != NULL && (!max_n->predot ||
119 0 : max_n->predot > n)) {
120 166 : max_n->predot = n;
121 : }
122 166 : return null_match(p);
123 :
124 540 : case '?':
125 : /* a '?' matches any single character */
126 540 : if (! *n) {
127 44 : return -1;
128 : }
129 495 : next_codepoint(n, &size_n);
130 495 : n += size_n;
131 495 : break;
132 :
133 323 : case '>':
134 : /* a '?' matches any single character, but
135 : treats '.' specially */
136 323 : if (n[0] == '.') {
137 104 : if (! n[1] && null_match(p) == 0) {
138 16 : return 0;
139 : }
140 88 : break;
141 : }
142 220 : if (! *n) return null_match(p);
143 187 : next_codepoint(n, &size_n);
144 187 : n += size_n;
145 187 : break;
146 :
147 816 : case '"':
148 : /* a bit like a soft '.' */
149 816 : if (*n == 0 && null_match(p) == 0) {
150 4 : return 0;
151 : }
152 812 : if (*n != '.') return -1;
153 154 : next_codepoint(n, &size_n);
154 154 : n += size_n;
155 154 : break;
156 :
157 962173 : default:
158 962173 : c2 = next_codepoint(n, &size_n);
159 962173 : if (c != c2) {
160 779166 : if (is_case_sensitive) {
161 1380 : return -1;
162 : }
163 777751 : if (codepoint_cmpi(c, c2) != 0) {
164 777631 : return -1;
165 : }
166 : }
167 183087 : n += size_n;
168 183087 : break;
169 : }
170 : }
171 :
172 9315997 : if (! *n) {
173 168 : return 0;
174 : }
175 :
176 9314874 : return -1;
177 : }
178 :
179 16428228 : int ms_fnmatch_protocol(const char *pattern, const char *string, int protocol,
180 : bool is_case_sensitive)
181 : {
182 16428228 : int ret = -1;
183 800193 : size_t count, i;
184 :
185 16428228 : if (strcmp(string, "..") == 0) {
186 3364 : string = ".";
187 : }
188 :
189 16428228 : if (strpbrk(pattern, "<>*?\"") == NULL) {
190 : /* this is not just an optimisation - it is essential
191 : for LANMAN1 correctness */
192 15262573 : return strcasecmp_m(pattern, string);
193 : }
194 :
195 1165655 : 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 5082470 : for (count=i=0;pattern[i];i++) {
225 3917100 : if (pattern[i] == '*' || pattern[i] == '<') count++;
226 : }
227 :
228 : /* If the pattern includes '*' or '<' */
229 1165370 : if (count >= 1) {
230 1165171 : struct max_n max_n[count];
231 :
232 1165171 : memset(max_n, 0, sizeof(struct max_n) * count);
233 :
234 1165171 : ret = ms_fnmatch_core(pattern, string, max_n, strrchr(string, '.'),
235 : is_case_sensitive);
236 : } else {
237 199 : ret = ms_fnmatch_core(pattern, string, NULL, strrchr(string, '.'),
238 : is_case_sensitive);
239 : }
240 :
241 1165085 : return ret;
242 : }
243 :
244 :
245 : /** a generic fnmatch function - uses for non-CIFS pattern matching */
246 15383953 : int gen_fnmatch(const char *pattern, const char *string)
247 : {
248 15383953 : return ms_fnmatch_protocol(pattern, string, PROTOCOL_NT1, false);
249 : }
|