LCOV - code coverage report
Current view: top level - lib/util - ms_fnmatch.c (source / functions) Hit Total Coverage
Test: coverage report for fix-15632 9995c5c2 Lines: 111 116 95.7 %
Date: 2024-04-13 12:30:31 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) 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     1031228 : static int null_match(const char *p)
      36             : {
      37     1032417 :         for (;*p;p++) {
      38        4043 :                 if (*p != '*' &&
      39        3576 :                     *p != '<' &&
      40        3342 :                     *p != '"' &&
      41        3115 :                     *p != '>') return -1;
      42             :         }
      43     1028144 :         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    11192704 : 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    11377524 :         while ((c = next_codepoint(p, &size))) {
      71     2076401 :                 p += size;
      72             : 
      73     2076401 :                 switch (c) {
      74     1110919 :                 case '*':
      75             :                         /* a '*' matches zero or more characters of any type */
      76     1110919 :                         if (max_n != NULL && max_n->predot &&
      77         338 :                             max_n->predot <= n) {
      78         338 :                                 return null_match(p);
      79             :                         }
      80    11056042 :                         for (i=0; n[i]; i += size_n) {
      81    10025836 :                                 next_codepoint(n+i, &size_n);
      82    10025836 :                                 if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) {
      83       80368 :                                         return 0;
      84             :                                 }
      85             :                         }
      86     1030206 :                         if (max_n != NULL && (!max_n->predot ||
      87           0 :                             max_n->predot > n)) {
      88     1030206 :                                 max_n->predot = n;
      89             :                         }
      90     1030206 :                         return null_match(p);
      91             : 
      92         916 :                 case '<':
      93             :                         /* a '<' matches zero or more characters of
      94             :                            any type, but stops matching at the last
      95             :                            '.' in the string. */
      96         916 :                         if (max_n != NULL && max_n->predot &&
      97         267 :                             max_n->predot <= n) {
      98         267 :                                 return null_match(p);
      99             :                         }
     100         649 :                         if (max_n != NULL && max_n->postdot &&
     101         214 :                             max_n->postdot <= n && n <= ldot) {
     102         182 :                                 return -1;
     103             :                         }
     104        3328 :                         for (i=0; n[i]; i += size_n) {
     105        3155 :                                 next_codepoint(n+i, &size_n);
     106        3155 :                                 if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) return 0;
     107        3091 :                                 if (n+i == ldot) {
     108         230 :                                         if (ms_fnmatch_core(p, n+i+size_n, max_n+1, ldot, is_case_sensitive) == 0) return 0;
     109         222 :                                         if (max_n != NULL) {
     110         222 :                                                 if (!max_n->postdot ||
     111           0 :                                                     max_n->postdot > n) {
     112         222 :                                                         max_n->postdot = n;
     113             :                                                 }
     114             :                                         }
     115         222 :                                         return -1;
     116             :                                 }
     117             :                         }
     118         173 :                         if (max_n != NULL && (!max_n->predot ||
     119           0 :                             max_n->predot > n)) {
     120         173 :                                 max_n->predot = n;
     121             :                         }
     122         173 :                         return null_match(p);
     123             : 
     124         831 :                 case '?':
     125             :                         /* a '?' matches any single character */
     126         831 :                         if (! *n) {
     127          58 :                                 return -1;
     128             :                         }
     129         772 :                         next_codepoint(n, &size_n);
     130         772 :                         n += size_n;
     131         772 :                         break;
     132             : 
     133         730 :                 case '>':
     134             :                         /* a '?' matches any single character, but
     135             :                            treats '.' specially */
     136         730 :                         if (n[0] == '.') {
     137         124 :                                 if (! n[1] && null_match(p) == 0) {
     138          16 :                                         return 0;
     139             :                                 }
     140         108 :                                 break;
     141             :                         }
     142         607 :                         if (! *n) return null_match(p);
     143         526 :                         next_codepoint(n, &size_n);
     144         526 :                         n += size_n;
     145         526 :                         break;
     146             : 
     147        1004 :                 case '"':
     148             :                         /* a bit like a soft '.' */
     149        1004 :                         if (*n == 0 && null_match(p) == 0) {
     150           0 :                                 return 0;
     151             :                         }
     152        1004 :                         if (*n != '.') return -1;
     153         203 :                         next_codepoint(n, &size_n);
     154         203 :                         n += size_n;
     155         203 :                         break;
     156             : 
     157      962001 :                 default:
     158      962001 :                         c2 = next_codepoint(n, &size_n);
     159      962001 :                         if (c != c2) {
     160      778877 :                                 if (is_case_sensitive) {
     161        1380 :                                         return -1;
     162             :                                 }
     163      777462 :                                 if (codepoint_cmpi(c, c2) != 0) {
     164      777335 :                                         return -1;
     165             :                                 }
     166             :                         }
     167      183211 :                         n += size_n;
     168      183211 :                         break;
     169             :                 }
     170             :         }
     171             : 
     172     9301123 :         if (! *n) {
     173         166 :                 return 0;
     174             :         }
     175             : 
     176     9300002 :         return -1;
     177             : }
     178             : 
     179    16420553 : int ms_fnmatch_protocol(const char *pattern, const char *string, int protocol,
     180             :                         bool is_case_sensitive)
     181             : {
     182    16420553 :         int ret = -1;
     183      800193 :         size_t count, i;
     184             : 
     185    16420553 :         if (strcmp(string, "..") == 0) {
     186        3369 :                 string = ".";
     187             :         }
     188             : 
     189    16420553 :         if (strpbrk(pattern, "<>*?\"") == NULL) {
     190             :                 /* this is not just an optimisation - it is essential
     191             :                    for LANMAN1 correctness */
     192    15256785 :                 return strcasecmp_m(pattern, string);
     193             :         }
     194             : 
     195     1163768 :         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     5077971 :         for (count=i=0;pattern[i];i++) {
     225     3914488 :                 if (pattern[i] == '*' || pattern[i] == '<') count++;
     226             :         }
     227             : 
     228             :         /* If the pattern includes '*' or '<' */
     229     1163483 :         if (count >= 1) {
     230     1163225 :                 struct max_n max_n[count];
     231             : 
     232     1163225 :                 memset(max_n, 0, sizeof(struct max_n) * count);
     233             : 
     234     1163225 :                 ret = ms_fnmatch_core(pattern, string, max_n, strrchr(string, '.'),
     235             :                                       is_case_sensitive);
     236             :         } else {
     237         258 :                 ret = ms_fnmatch_core(pattern, string, NULL, strrchr(string, '.'),
     238             :                                       is_case_sensitive);
     239             :         }
     240             : 
     241     1163198 :         return ret;
     242             : }
     243             : 
     244             : 
     245             : /** a generic fnmatch function - uses for non-CIFS pattern matching */
     246    15378397 : int gen_fnmatch(const char *pattern, const char *string)
     247             : {
     248    15378397 :         return ms_fnmatch_protocol(pattern, string, PROTOCOL_NT1, false);
     249             : }

Generated by: LCOV version 1.14