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 : #include "includes.h"
28 :
29 24155 : static int null_match(const smb_ucs2_t *p)
30 : {
31 24155 : for (;*p;p++) {
32 0 : if (*p != UCS2_CHAR('*') &&
33 0 : *p != UCS2_CHAR('<') &&
34 0 : *p != UCS2_CHAR('"') &&
35 0 : *p != UCS2_CHAR('>')) return -1;
36 : }
37 24155 : return 0;
38 : }
39 :
40 : /*
41 : the max_n structure is purely for efficiency, it doesn't contribute
42 : to the matching algorithm except by ensuring that the algorithm does
43 : not grow exponentially
44 : */
45 : struct max_n {
46 : const smb_ucs2_t *predot;
47 : const smb_ucs2_t *postdot;
48 : };
49 :
50 :
51 : /*
52 : p and n are the pattern and string being matched. The max_n array is
53 : an optimisation only. The ldot pointer is NULL if the string does
54 : not contain a '.', otherwise it points at the last dot in 'n'.
55 : */
56 124891 : static int ms_fnmatch_core(const smb_ucs2_t *p, const smb_ucs2_t *n,
57 : struct max_n *max_n, const smb_ucs2_t *ldot,
58 : bool is_case_sensitive)
59 : {
60 0 : smb_ucs2_t c;
61 0 : int i;
62 :
63 127771 : while ((c = *p++)) {
64 32555 : switch (c) {
65 : /* a '*' matches zero or more characters of any type */
66 24155 : case UCS2_CHAR('*'):
67 24155 : if (max_n->predot && max_n->predot <= n) {
68 0 : return null_match(p);
69 : }
70 118891 : for (i=0; n[i]; i++) {
71 94736 : if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) {
72 0 : return 0;
73 : }
74 : }
75 24155 : if (!max_n->predot || max_n->predot > n) max_n->predot = n;
76 24155 : return null_match(p);
77 :
78 : /* a '<' matches zero or more characters of
79 : any type, but stops matching at the last
80 : '.' in the string. */
81 1440 : case UCS2_CHAR('<'):
82 1440 : if (max_n->predot && max_n->predot <= n) {
83 0 : return null_match(p);
84 : }
85 1440 : if (max_n->postdot && max_n->postdot <= n && n <= ldot) {
86 0 : return -1;
87 : }
88 3600 : for (i=0; n[i]; i++) {
89 3600 : if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) return 0;
90 3120 : if (n+i == ldot) {
91 960 : if (ms_fnmatch_core(p, n+i+1, max_n+1, ldot, is_case_sensitive) == 0) return 0;
92 960 : if (!max_n->postdot || max_n->postdot > n) max_n->postdot = n;
93 960 : return -1;
94 : }
95 : }
96 0 : if (!max_n->predot || max_n->predot > n) max_n->predot = n;
97 0 : return null_match(p);
98 :
99 : /* a '?' matches any single character */
100 0 : case UCS2_CHAR('?'):
101 0 : if (! *n) {
102 0 : return -1;
103 : }
104 0 : n++;
105 0 : break;
106 :
107 : /* a '?' matches any single character */
108 0 : case UCS2_CHAR('>'):
109 0 : if (n[0] == UCS2_CHAR('.')) {
110 0 : if (! n[1] && null_match(p) == 0) {
111 0 : return 0;
112 : }
113 0 : break;
114 : }
115 0 : if (! *n) return null_match(p);
116 0 : n++;
117 0 : break;
118 :
119 0 : case UCS2_CHAR('"'):
120 0 : if (*n == 0 && null_match(p) == 0) {
121 0 : return 0;
122 : }
123 0 : if (*n != UCS2_CHAR('.')) return -1;
124 0 : n++;
125 0 : break;
126 :
127 6960 : default:
128 6960 : if (c != *n) {
129 4080 : if (is_case_sensitive) {
130 0 : return -1;
131 : }
132 4080 : if (toupper_w(c) != toupper_w(*n)) {
133 4080 : return -1;
134 : }
135 : }
136 2880 : n++;
137 2880 : break;
138 : }
139 : }
140 :
141 95216 : if (! *n) {
142 480 : return 0;
143 : }
144 :
145 94736 : return -1;
146 : }
147 :
148 25613 : int ms_fnmatch(const char *pattern, const char *string, bool translate_pattern,
149 : bool is_case_sensitive)
150 : {
151 25613 : smb_ucs2_t *p = NULL;
152 25613 : smb_ucs2_t *s = NULL;
153 0 : int ret;
154 0 : size_t count, i;
155 25613 : struct max_n *max_n = NULL;
156 25613 : struct max_n *max_n_free = NULL;
157 0 : struct max_n one_max_n;
158 0 : size_t converted_size;
159 :
160 25613 : if (ISDOTDOT(string)) {
161 0 : string = ".";
162 : }
163 :
164 25613 : if (strpbrk(pattern, "<>*?\"") == NULL) {
165 : /* this is not just an optimisation - it is essential
166 : for LANMAN1 correctness */
167 18 : if (is_case_sensitive) {
168 0 : return strcmp(pattern, string);
169 : } else {
170 18 : return strcasecmp_m(pattern, string);
171 : }
172 : }
173 :
174 25595 : if (!push_ucs2_talloc(talloc_tos(), &p, pattern, &converted_size)) {
175 0 : return -1;
176 : }
177 :
178 25595 : if (!push_ucs2_talloc(talloc_tos(), &s, string, &converted_size)) {
179 0 : TALLOC_FREE(p);
180 0 : return -1;
181 : }
182 :
183 25595 : if (translate_pattern) {
184 : /*
185 : for older negotiated protocols it is possible to
186 : translate the pattern to produce a "new style"
187 : pattern that exactly matches w2k behaviour
188 : */
189 56950 : for (i=0;p[i];i++) {
190 31355 : if (p[i] == UCS2_CHAR('?')) {
191 0 : p[i] = UCS2_CHAR('>');
192 31355 : } else if (p[i] == UCS2_CHAR('.') &&
193 1440 : (p[i+1] == UCS2_CHAR('?') ||
194 1440 : p[i+1] == UCS2_CHAR('*') ||
195 1440 : p[i+1] == 0)) {
196 0 : p[i] = UCS2_CHAR('"');
197 31355 : } else if (p[i] == UCS2_CHAR('*') && p[i+1] == UCS2_CHAR('.')) {
198 1440 : p[i] = UCS2_CHAR('<');
199 : }
200 : }
201 : }
202 :
203 56950 : for (count=i=0;p[i];i++) {
204 31355 : if (p[i] == UCS2_CHAR('*') || p[i] == UCS2_CHAR('<')) count++;
205 : }
206 :
207 25595 : if (count != 0) {
208 25595 : if (count == 1) {
209 : /*
210 : * We're doing this a LOT, so save the effort to allocate
211 : */
212 25595 : ZERO_STRUCT(one_max_n);
213 25595 : max_n = &one_max_n;
214 : }
215 : else {
216 0 : max_n = SMB_CALLOC_ARRAY(struct max_n, count);
217 0 : if (!max_n) {
218 0 : TALLOC_FREE(p);
219 0 : TALLOC_FREE(s);
220 0 : return -1;
221 : }
222 0 : max_n_free = max_n;
223 : }
224 : }
225 :
226 25595 : ret = ms_fnmatch_core(p, s, max_n, strrchr_w(s, UCS2_CHAR('.')), is_case_sensitive);
227 :
228 25595 : SAFE_FREE(max_n_free);
229 25595 : TALLOC_FREE(p);
230 25595 : TALLOC_FREE(s);
231 25595 : return ret;
232 : }
|