LCOV - code coverage report
Current view: top level - lib/util - ms_fnmatch.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 108 112 96.4 %
Date: 2021-09-23 10:06:22 Functions: 4 4 100.0 %

          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 "includes.h"
      32             : #include "libcli/smb/smb_constants.h"
      33             : 
      34      640687 : static int null_match(const char *p)
      35             : {
      36      641398 :         for (;*p;p++) {
      37        7534 :                 if (*p != '*' &&
      38        7323 :                     *p != '<' &&
      39        7007 :                     *p != '"' &&
      40        6506 :                     *p != '>') return -1;
      41             :         }
      42      637579 :         return 0;
      43             : }
      44             : 
      45             : /*
      46             :   the max_n structure is purely for efficiency, it doesn't contribute
      47             :   to the matching algorithm except by ensuring that the algorithm does
      48             :   not grow exponentially
      49             : */
      50             : struct max_n {
      51             :         const char *predot;
      52             :         const char *postdot;
      53             : };
      54             : 
      55             : 
      56             : /*
      57             :   p and n are the pattern and string being matched. The max_n array is
      58             :   an optimisation only. The ldot pointer is NULL if the string does
      59             :   not contain a '.', otherwise it points at the last dot in 'n'.
      60             : */
      61     7621519 : static int ms_fnmatch_core(const char *p, const char *n, 
      62             :                            struct max_n *max_n, const char *ldot,
      63             :                            bool is_case_sensitive)
      64             : {
      65             :         codepoint_t c, c2;
      66             :         int i;
      67             :         size_t size, size_n;
      68             : 
      69    14218141 :         while ((c = next_codepoint(p, &size))) {
      70     1687238 :                 p += size;
      71             : 
      72     1687238 :                 switch (c) {
      73      720907 :                 case '*':
      74             :                         /* a '*' matches zero or more characters of any type */
      75      721028 :                         if (max_n != NULL && max_n->predot &&
      76         121 :                             max_n->predot <= n) {
      77         121 :                                 return null_match(p);
      78             :                         }
      79     7489622 :                         for (i=0; n[i]; i += size_n) {
      80     6849554 :                                 next_codepoint(n+i, &size_n);
      81     6849554 :                                 if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) {
      82       80558 :                                         return 0;
      83             :                                 }
      84             :                         }
      85      640218 :                         if (max_n != NULL && (!max_n->predot ||
      86           0 :                             max_n->predot > n)) {
      87      640218 :                                 max_n->predot = n;
      88             :                         }
      89      640078 :                         return null_match(p);
      90             : 
      91         451 :                 case '<':
      92             :                         /* a '<' matches zero or more characters of
      93             :                            any type, but stops matching at the last
      94             :                            '.' in the string. */
      95         545 :                         if (max_n != NULL && max_n->predot &&
      96          94 :                             max_n->predot <= n) {
      97          94 :                                 return null_match(p);
      98             :                         }
      99         377 :                         if (max_n != NULL && max_n->postdot &&
     100          40 :                             max_n->postdot <= n && n <= ldot) {
     101          18 :                                 return -1;
     102             :                         }
     103        2503 :                         for (i=0; n[i]; i += size_n) {
     104        2395 :                                 next_codepoint(n+i, &size_n);
     105        2395 :                                 if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) return 0;
     106        2323 :                                 if (n+i == ldot) {
     107         147 :                                         if (ms_fnmatch_core(p, n+i+size_n, max_n+1, ldot, is_case_sensitive) == 0) return 0;
     108         129 :                                         if (max_n != NULL) {
     109         129 :                                                 if (!max_n->postdot ||
     110           0 :                                                     max_n->postdot > n) {
     111         129 :                                                         max_n->postdot = n;
     112             :                                                 }
     113             :                                         }
     114         129 :                                         return -1;
     115             :                                 }
     116             :                         }
     117         120 :                         if (max_n != NULL && (!max_n->predot ||
     118           0 :                             max_n->predot > n)) {
     119         120 :                                 max_n->predot = n;
     120             :                         }
     121         120 :                         return null_match(p);
     122             : 
     123         716 :                 case '?':
     124             :                         /* a '?' matches any single character */
     125         716 :                         if (! *n) {
     126          71 :                                 return -1;
     127             :                         }
     128         644 :                         next_codepoint(n, &size_n);
     129         644 :                         n += size_n;
     130         644 :                         break;
     131             : 
     132         586 :                 case '>':
     133             :                         /* a '?' matches any single character, but
     134             :                            treats '.' specially */
     135         586 :                         if (n[0] == '.') {
     136         111 :                                 if (! n[1] && null_match(p) == 0) {
     137           8 :                                         return 0;
     138             :                                 }
     139         103 :                                 break;
     140             :                         }
     141         475 :                         if (! *n) return null_match(p);
     142         395 :                         next_codepoint(n, &size_n);
     143         395 :                         n += size_n;
     144         395 :                         break;
     145             : 
     146        1064 :                 case '"':
     147             :                         /* a bit like a soft '.' */
     148        1064 :                         if (*n == 0 && null_match(p) == 0) {
     149          14 :                                 return 0;
     150             :                         }
     151        1050 :                         if (*n != '.') return -1;
     152         213 :                         next_codepoint(n, &size_n);
     153         213 :                         n += size_n;
     154         213 :                         break;
     155             : 
     156      963514 :                 default:
     157      963514 :                         c2 = next_codepoint(n, &size_n);
     158      963514 :                         if (c != c2) {
     159      780678 :                                 if (is_case_sensitive) {
     160        1380 :                                         return -1;
     161             :                                 }
     162      779263 :                                 if (codepoint_cmpi(c, c2) != 0) {
     163      779132 :                                         return -1;
     164             :                                 }
     165             :                         }
     166      182903 :                         n += size_n;
     167      182903 :                         break;
     168             :                 }
     169             :         }
     170             :         
     171     6118539 :         if (! *n) {
     172         170 :                 return 0;
     173             :         }
     174             :         
     175     6118351 :         return -1;
     176             : }
     177             : 
     178    14411123 : int ms_fnmatch_protocol(const char *pattern, const char *string, int protocol,
     179             :                         bool is_case_sensitive)
     180             : {
     181    14411123 :         int ret = -1;
     182             :         size_t count, i;
     183             : 
     184    14411123 :         if (strcmp(string, "..") == 0) {
     185        5452 :                 string = ".";
     186             :         }
     187             : 
     188    14411123 :         if (strpbrk(pattern, "<>*?\"") == NULL) {
     189             :                 /* this is not just an optimisation - it is essential
     190             :                    for LANMAN1 correctness */
     191    13641387 :                 return strcasecmp_m(pattern, string);
     192             :         }
     193             : 
     194      769736 :         if (protocol <= PROTOCOL_LANMAN2) {
     195         313 :                 char *p = talloc_strdup(NULL, pattern);
     196         313 :                 if (p == NULL) {
     197           0 :                         return -1;
     198             :                 }
     199             :                 /*
     200             :                   for older negotiated protocols it is possible to
     201             :                   translate the pattern to produce a "new style"
     202             :                   pattern that exactly matches w2k behaviour
     203             :                 */
     204        4417 :                 for (i=0;p[i];i++) {
     205        4157 :                         if (p[i] == '?') {
     206          13 :                                 p[i] = '>';
     207        4521 :                         } else if (p[i] == '.' && 
     208         711 :                                    (p[i+1] == '?' || 
     209         684 :                                     p[i+1] == '*' ||
     210         300 :                                     p[i+1] == 0)) {
     211          34 :                                 p[i] = '"';
     212        4441 :                         } else if (p[i] == '*' && 
     213         352 :                                    p[i+1] == '.') {
     214         256 :                                 p[i] = '<';
     215             :                         }
     216             :                 }
     217         313 :                 ret = ms_fnmatch_protocol(p, string, PROTOCOL_NT1,
     218             :                                           is_case_sensitive);
     219         313 :                 talloc_free(p);
     220         313 :                 return ret;
     221             :         }
     222             : 
     223     4213610 :         for (count=i=0;pattern[i];i++) {
     224     3444386 :                 if (pattern[i] == '*' || pattern[i] == '<') count++;
     225             :         }
     226             : 
     227             :         /* If the pattern includes '*' or '<' */
     228      769423 :         if (count >= 1) {
     229      769202 :                 struct max_n max_n[count];
     230             : 
     231      769395 :                 memset(max_n, 0, sizeof(struct max_n) * count);
     232             : 
     233      769202 :                 ret = ms_fnmatch_core(pattern, string, max_n, strrchr(string, '.'),
     234             :                                       is_case_sensitive);
     235             :         } else {
     236         221 :                 ret = ms_fnmatch_core(pattern, string, NULL, strrchr(string, '.'),
     237             :                                       is_case_sensitive);
     238             :         }
     239             : 
     240      769224 :         return ret;
     241             : }
     242             : 
     243             : 
     244             : /** a generic fnmatch function - uses for non-CIFS pattern matching */
     245    13750199 : int gen_fnmatch(const char *pattern, const char *string)
     246             : {
     247    13750199 :         return ms_fnmatch_protocol(pattern, string, PROTOCOL_NT1, false);
     248             : }

Generated by: LCOV version 1.13