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 33static int null_match(const char *p) 34{ 35 for (;*p;p++) { 36 if (*p != '*' && 37 *p != '<' && 38 *p != '"' && 39 *p != '>') return -1; 40 } 41 return 0; 42} 43 44/* 45 the max_n structure is purely for efficiency, it doesn't contribute 46 to the matching algorithm except by ensuring that the algorithm does 47 not grow exponentially 48*/ 49struct max_n { 50 const char *predot; 51 const char *postdot; 52}; 53 54 55/* 56 p and n are the pattern and string being matched. The max_n array is 57 an optimisation only. The ldot pointer is NULL if the string does 58 not contain a '.', otherwise it points at the last dot in 'n'. 59*/ 60static int ms_fnmatch_core(const char *p, const char *n, 61 struct max_n *max_n, const char *ldot) 62{ 63 codepoint_t c, c2; 64 int i; 65 size_t size, size_n; 66 67 while ((c = next_codepoint(p, &size))) { 68 p += size; 69 70 switch (c) { 71 case '*': 72 /* a '*' matches zero or more characters of any type */ 73 if (max_n->predot && max_n->predot <= n) { 74 return null_match(p); 75 } 76 for (i=0; n[i]; i += size_n) { 77 next_codepoint(n+i, &size_n); 78 if (ms_fnmatch_core(p, n+i, max_n+1, ldot) == 0) { 79 return 0; 80 } 81 } 82 if (!max_n->predot || max_n->predot > n) max_n->predot = n; 83 return null_match(p); 84 85 case '<': 86 /* a '<' matches zero or more characters of 87 any type, but stops matching at the last 88 '.' in the string. */ 89 if (max_n->predot && max_n->predot <= n) { 90 return null_match(p); 91 } 92 if (max_n->postdot && max_n->postdot <= n && n <= ldot) { 93 return -1; 94 } 95 for (i=0; n[i]; i += size_n) { 96 next_codepoint(n+i, &size_n); 97 if (ms_fnmatch_core(p, n+i, max_n+1, ldot) == 0) return 0; 98 if (n+i == ldot) { 99 if (ms_fnmatch_core(p, n+i+size_n, max_n+1, ldot) == 0) return 0; 100 if (!max_n->postdot || max_n->postdot > n) max_n->postdot = n; 101 return -1; 102 } 103 } 104 if (!max_n->predot || max_n->predot > n) max_n->predot = n; 105 return null_match(p); 106 107 case '?': 108 /* a '?' matches any single character */ 109 if (! *n) { 110 return -1; 111 } 112 next_codepoint(n, &size_n); 113 n += size_n; 114 break; 115 116 case '>': 117 /* a '?' matches any single character, but 118 treats '.' specially */ 119 if (n[0] == '.') { 120 if (! n[1] && null_match(p) == 0) { 121 return 0; 122 } 123 break; 124 } 125 if (! *n) return null_match(p); 126 next_codepoint(n, &size_n); 127 n += size_n; 128 break; 129 130 case '"': 131 /* a bit like a soft '.' */ 132 if (*n == 0 && null_match(p) == 0) { 133 return 0; 134 } 135 if (*n != '.') return -1; 136 next_codepoint(n, &size_n); 137 n += size_n; 138 break; 139 140 default: 141 c2 = next_codepoint(n, &size_n); 142 if (c != c2 && codepoint_cmpi(c, c2) != 0) { 143 return -1; 144 } 145 n += size_n; 146 break; 147 } 148 } 149 150 if (! *n) { 151 return 0; 152 } 153 154 return -1; 155} 156 157int ms_fnmatch(const char *pattern, const char *string, enum protocol_types protocol) 158{ 159 int ret, count, i; 160 struct max_n *max_n = NULL; 161 162 if (strcmp(string, "..") == 0) { 163 string = "."; 164 } 165 166 if (strpbrk(pattern, "<>*?\"") == NULL) { 167 /* this is not just an optimisation - it is essential 168 for LANMAN1 correctness */ 169 return strcasecmp_m(pattern, string); 170 } 171 172 if (protocol <= PROTOCOL_LANMAN2) { 173 char *p = talloc_strdup(NULL, pattern); 174 if (p == NULL) { 175 return -1; 176 } 177 /* 178 for older negotiated protocols it is possible to 179 translate the pattern to produce a "new style" 180 pattern that exactly matches w2k behaviour 181 */ 182 for (i=0;p[i];i++) { 183 if (p[i] == '?') { 184 p[i] = '>'; 185 } else if (p[i] == '.' && 186 (p[i+1] == '?' || 187 p[i+1] == '*' || 188 p[i+1] == 0)) { 189 p[i] = '"'; 190 } else if (p[i] == '*' && 191 p[i+1] == '.') { 192 p[i] = '<'; 193 } 194 } 195 ret = ms_fnmatch(p, string, PROTOCOL_NT1); 196 talloc_free(p); 197 return ret; 198 } 199 200 for (count=i=0;pattern[i];i++) { 201 if (pattern[i] == '*' || pattern[i] == '<') count++; 202 } 203 204 max_n = talloc_zero_array(NULL, struct max_n, count); 205 if (max_n == NULL) { 206 return -1; 207 } 208 209 ret = ms_fnmatch_core(pattern, string, max_n, strrchr(string, '.')); 210 211 talloc_free(max_n); 212 213 return ret; 214} 215 216 217/** a generic fnmatch function - uses for non-CIFS pattern matching */ 218int gen_fnmatch(const char *pattern, const char *string) 219{ 220 return ms_fnmatch(pattern, string, PROTOCOL_NT1); 221} 222