From 71e7974f3f847759ba6f844ea7f482786cc5db02 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sun, 30 Apr 2000 04:45:16 +0000 Subject: YIPEE!!!!! We finally have a perfect emulation of Microsoft wildcard matching. The routine ms_fnmatch() does wildcard matching with all MS wildcards (including the unicode wildcards), and masktest against a NT4 workstation with hundreds of thousands of random exmaples has not found a single error. amazingly it is only about 60 lines of code, but it has taken us years to get it right. I didn't sleep much last night :) (This used to be commit cc9e007cdfdd300189f89e2a55e4234e47fa842d) --- source3/Makefile.in | 1 + source3/include/client.h | 1 + source3/lib/ms_fnmatch.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++ source3/lib/util.c | 2 +- source3/libsmb/clilist.c | 1 + source3/utils/masktest.c | 94 ++++++------------------------ 6 files changed, 166 insertions(+), 79 deletions(-) create mode 100644 source3/lib/ms_fnmatch.c diff --git a/source3/Makefile.in b/source3/Makefile.in index 018927b793..1d551b5443 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -109,6 +109,7 @@ LIB_OBJ = lib/charcnv.o lib/charset.o lib/debug.o lib/fault.o \ lib/util_unistr.o lib/util_file.o \ lib/util.o lib/util_sock.o lib/util_sec.o smbd/ssl.o lib/fnmatch.o \ lib/talloc.o lib/hash.o lib/substitute.o lib/fsusage.o \ + lib/ms_fnmatch.o \ $(TDB_OBJ) UBIQX_OBJ = ubiqx/ubi_BinTree.o ubiqx/ubi_Cache.o ubiqx/ubi_SplayTree.o \ diff --git a/source3/include/client.h b/source3/include/client.h index 2a780ece26..406f2e972c 100644 --- a/source3/include/client.h +++ b/source3/include/client.h @@ -44,6 +44,7 @@ typedef struct file_info time_t atime; time_t ctime; pstring name; + char short_name[13]; } file_info; struct print_job_info diff --git a/source3/lib/ms_fnmatch.c b/source3/lib/ms_fnmatch.c new file mode 100644 index 0000000000..d9475cb6ef --- /dev/null +++ b/source3/lib/ms_fnmatch.c @@ -0,0 +1,146 @@ +/* + Unix SMB/Netbios implementation. + Version 3.0 + + Copyright (C) Andrew Tridgell 1992-1998 + + This module is derived from fnmatch.c copyright by the Free + Software Foundation. It has been extensively modified to implement + the wildcard matcing semantics of Microsoft SMB servers. + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* this matches only filenames, with no directory component */ +#if FNMATCH_TEST +#include +#include +#else +#include "includes.h" +#endif + +/* the following function was derived using the masktest utility - + after years of effort we finally have a perfect MS wildcard + matching routine! */ +int ms_fnmatch(char *pattern, char *string) +{ + char *p = pattern, *n = string; + char c; + + while ((c = *p++)) { + switch (c) { + case '?': + if (! *n) return -1; + n++; + break; + + case '>': + if (n[0] == '.') { + if (! n[1] && ms_fnmatch(p, n+1) == 0) return 0; + if (ms_fnmatch(p, n) == 0) return 0; + return -1; + } + if (! *n) return ms_fnmatch(p, n); + n++; + break; + + case '*': + for (; *n; n++) { + if (ms_fnmatch(p, n) == 0) return 0; + } + break; + + case '<': + for (; *n; n++) { + if (ms_fnmatch(p, n) == 0) return 0; + if (*n == '.' && !strchr(n+1,'.')) { + n++; + break; + } + } + break; + + case '"': + if (*n == 0 && ms_fnmatch(p, n) == 0) return 0; + if (*n != '.') return -1; + n++; + break; + + default: + if (c != *n) return -1; + n++; + } + } + + if (! *n) return 0; + + return -1; +} + + +#if FNMATCH_TEST + +static int match_one(char *pattern, char *file) +{ + if (strcmp(file,"..") == 0) file = "."; + if (strcmp(pattern,".") == 0) return -1; + + return ms_fnmatch(pattern, file); +} + +static char *match_test(char *pattern, char *file, char *short_name) +{ + static char ret[4]; + strncpy(ret, "---", 3); + + if (match_one(pattern, ".") == 0) ret[0] = '+'; + if (match_one(pattern, "..") == 0) ret[1] = '+'; + if (match_one(pattern, file) == 0 || + (*short_name && match_one(pattern, short_name)==0)) ret[2] = '+'; + return ret; +} + + int main(int argc, char *argv[]) +{ + int ret; + char ans[4], mask[100], file[100], mfile[100]; + char *ans2; + int n, i=0; + char line[200]; + + if (argc == 3) { + ret = ms_fnmatch(argv[1], argv[2]); + if (ret == 0) + printf("YES\n"); + else printf("NO\n"); + return ret; + } + mfile[0] = 0; + + while (fgets(line, sizeof(line)-1, stdin)) { + n = sscanf(line, "%3s %s %s %s\n", ans, mask, file, mfile); + if (n < 3) continue; + ans2 = match_test(mask, file, mfile); + if (strcmp(ans2, ans)) { + printf("%s %s %d mask=[%s] file=[%s] mfile=[%s]\n", + ans, ans2, i, mask, file, mfile); + } + i++; + mfile[0] = 0; + } + return 0; +} +#endif diff --git a/source3/lib/util.c b/source3/lib/util.c index 981dd51f9d..7d9f6a5a50 100644 --- a/source3/lib/util.c +++ b/source3/lib/util.c @@ -63,7 +63,7 @@ extern int DEBUGLEVEL; int Protocol = PROTOCOL_COREPLUS; /* a default finfo structure to ensure all fields are sensible */ -file_info def_finfo = {-1,0,0,0,0,0,0,""}; +file_info def_finfo = {-1,0,0,0,0,0,0,"",""}; /* this is used by the chaining code */ int chain_size = 0; diff --git a/source3/libsmb/clilist.c b/source3/libsmb/clilist.c index 4cb477961b..f2e42be523 100644 --- a/source3/libsmb/clilist.c +++ b/source3/libsmb/clilist.c @@ -123,6 +123,7 @@ static int interpret_long_filename(int level,char *p,file_info *finfo) namelen = IVAL(p,0); p += 4; p += 4; /* EA size */ p += 2; /* short name len? */ + unistr_to_ascii(finfo->short_name, p, 12); p += 24; /* short name? */ StrnCpy(finfo->name,p,MIN(sizeof(finfo->name)-1,namelen)); dos_to_unix(finfo->name,True); diff --git a/source3/utils/masktest.c b/source3/utils/masktest.c index f7b6ad1b1b..d1a8ddce4b 100644 --- a/source3/utils/masktest.c +++ b/source3/utils/masktest.c @@ -43,82 +43,15 @@ char *standard_files[] = {"abc", "abc.", ".abc", NULL}; -#include - static BOOL reg_match_one(char *pattern, char *file) { - pstring rpattern; - pstring rfile; - - pstrcpy(rpattern, pattern); - - if (strcmp(file,"..") == 0) file = "."; - if (strcmp(rpattern,".") == 0) return False; - - all_string_sub(rpattern,"\"", ".", 0); - all_string_sub(rpattern,"<", "*", 0); - - all_string_sub(rpattern,"*>", "*", 0); - all_string_sub(rpattern,">*", "*", 0); - all_string_sub(rpattern,">", "?", 0); - - if (is_8_3(file, False)) { - return fnmatch(rpattern, file, 0)==0; - } - - pstrcpy(rfile, file); - mangle_name_83(rfile); - strlower(rfile); - - return (fnmatch(rpattern, file, 0)==0 || - fnmatch(rpattern, rfile, 0)==0); -} - -static BOOL regex_reg_match_one(char *pattern, char *file) -{ - pstring rpattern; - regex_t preg; - BOOL ret = False; - - return fnmatch(pattern, file, 0)==0; - if (strcmp(file,"..") == 0) file = "."; if (strcmp(pattern,".") == 0) return False; - if (strcmp(pattern,"") == 0) { - if (strcmp(file,".") == 0) return False; - return True; - } - - pstrcpy(rpattern,"^"); - pstrcat(rpattern, pattern); - - all_string_sub(rpattern,".", "[.]", 0); - all_string_sub(rpattern,"?", ".{1}", 0); - all_string_sub(rpattern,"*", ".*", 0); - all_string_sub(rpattern+strlen(rpattern)-1,">", "([^.]?|[.]?$)", 0); - all_string_sub(rpattern,">", "[^.]?", 0); - - all_string_sub(rpattern,"<[.]", ".*[.]", 0); - all_string_sub(rpattern,"<\"", "(.*[.]|.*$)", 0); - all_string_sub(rpattern,"<", "([^.]*|[^.]*[.]|[.][^.]*|[.].*[.])", 0); - if (strlen(pattern)>1) { - all_string_sub(rpattern+strlen(rpattern)-1,"\"", "[.]?", 0); - } - all_string_sub(rpattern,"\"", "([.]|$)", 0); - pstrcat(rpattern,"$"); - - /* printf("pattern=[%s] rpattern=[%s]\n", pattern, rpattern); */ - - regcomp(&preg, rpattern, REG_ICASE|REG_NOSUB|REG_EXTENDED); - ret = (regexec(&preg, file, 0, NULL, 0) == 0); - - regfree(&preg); - - return ret; + return ms_fnmatch(pattern, file)==0; } -static char *reg_test(char *pattern, char *file) +static char *reg_test(char *pattern, char *file, char *short_name) { static fstring ret; fstrcpy(ret, "---"); @@ -128,7 +61,8 @@ static char *reg_test(char *pattern, char *file) if (reg_match_one(pattern, ".")) ret[0] = '+'; if (reg_match_one(pattern, "..")) ret[1] = '+'; - if (reg_match_one(pattern, file)) ret[2] = '+'; + if (reg_match_one(pattern, file) || + (*short_name && reg_match_one(pattern, short_name))) ret[2] = '+'; return ret; } @@ -228,6 +162,7 @@ struct cli_state *connect_one(char *share) } static char *resultp; +static file_info *finfo; void listfn(file_info *f, const char *s) { @@ -238,6 +173,7 @@ void listfn(file_info *f, const char *s) } else { resultp[2] = '+'; } + finfo = f; } @@ -248,12 +184,14 @@ static void testpair(struct cli_state *cli1, struct cli_state *cli2, fstring res1, res2; char *res3; static int count; + fstring short_name; count++; fstrcpy(res1, "---"); fstrcpy(res2, "---"); + fnum = cli_open(cli1, file, O_CREAT|O_TRUNC|O_RDWR, 0); if (fnum == -1) { DEBUG(0,("Can't create %s on cli1\n", file)); @@ -269,22 +207,22 @@ static void testpair(struct cli_state *cli1, struct cli_state *cli2, cli_close(cli2, fnum); resultp = res1; + fstrcpy(short_name, ""); + finfo = NULL; cli_list(cli1, mask, aHIDDEN | aDIR, listfn); + if (finfo) { + fstrcpy(short_name, finfo->short_name); + strlower(short_name); + } - res3 = reg_test(mask, file); + res3 = reg_test(mask, file, short_name); resultp = res2; cli_list(cli2, mask, aHIDDEN | aDIR, listfn); if (showall || strcmp(res1, res3)) { - char *p; - pstring rfile; - p = strrchr(file,'\\'); - pstrcpy(rfile, p+1); - mangle_name_83(rfile); - strlower(rfile); DEBUG(0,("%s %s %s %d mask=[%s] file=[%s] mfile=[%s]\n", - res1, res2, res3, count, mask, file, rfile)); + res1, res2, res3, count, mask, file, short_name)); } cli_unlink(cli1, file); -- cgit