Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/lib/util/glob.c
1532 views
1
/*
2
* SPDX-License-Identifier: BSD-3-Clause
3
*
4
* Copyright (c) 2008-2014 Todd C. Miller <[email protected]>
5
* Copyright (c) 1989, 1993
6
* The Regents of the University of California. All rights reserved.
7
*
8
* This code is derived from software contributed to Berkeley by
9
* Guido van Rossum.
10
*
11
* Redistribution and use in source and binary forms, with or without
12
* modification, are permitted provided that the following conditions
13
* are met:
14
* 1. Redistributions of source code must retain the above copyright
15
* notice, this list of conditions and the following disclaimer.
16
* 2. Redistributions in binary form must reproduce the above copyright
17
* notice, this list of conditions and the following disclaimer in the
18
* documentation and/or other materials provided with the distribution.
19
* 3. Neither the name of the University nor the names of its contributors
20
* may be used to endorse or promote products derived from this software
21
* without specific prior written permission.
22
*
23
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33
* SUCH DAMAGE.
34
*
35
* @(#)glob.c 8.3 (Berkeley) 10/13/93
36
*/
37
38
/*
39
* glob(3) -- a superset of the one defined in POSIX 1003.2.
40
*
41
* The [!...] convention to negate a range is supported (SysV, Posix, ksh).
42
*
43
* Optional extra services, controlled by flags not defined by POSIX:
44
*
45
* GLOB_MAGCHAR:
46
* Set in gl_flags if pattern contained a globbing character.
47
* GLOB_TILDE:
48
* expand ~user/foo to the /home/dir/of/user/foo
49
* GLOB_BRACE:
50
* expand {1,2}{a,b} to 1a 1b 2a 2b
51
* gl_matchc:
52
* Number of matches in the current invocation of glob.
53
*/
54
55
#include <config.h>
56
57
#ifndef HAVE_GLOB
58
59
#include <sys/stat.h>
60
61
#include <stdio.h>
62
#include <stdlib.h>
63
#include <string.h>
64
#include <unistd.h>
65
#if defined(HAVE_STDINT_H)
66
# include <stdint.h>
67
#elif defined(HAVE_INTTYPES_H)
68
# include <inttypes.h>
69
#endif
70
#include <ctype.h>
71
#include <dirent.h>
72
#include <errno.h>
73
#include <limits.h>
74
#include <pwd.h>
75
76
#include <sudo_compat.h>
77
#include <compat/glob.h>
78
#include <compat/charclass.h>
79
80
#define DOLLAR '$'
81
#define DOT '.'
82
#define EOS '\0'
83
#define LBRACKET '['
84
#define NOT '!'
85
#define QUESTION '?'
86
#define QUOTE '\\'
87
#define RANGE '-'
88
#define RBRACKET ']'
89
#define SEP '/'
90
#define STAR '*'
91
#define TILDE '~'
92
#define UNDERSCORE '_'
93
#define LBRACE '{'
94
#define RBRACE '}'
95
#define SLASH '/'
96
#define COMMA ','
97
98
#ifndef DEBUG
99
100
#define M_QUOTE 0x8000
101
#define M_PROTECT 0x4000
102
#define M_MASK 0xffff
103
#define M_ASCII 0x00ff
104
105
typedef unsigned short Char;
106
107
#else
108
109
#define M_QUOTE 0x80
110
#define M_PROTECT 0x40
111
#define M_MASK 0xff
112
#define M_ASCII 0x7f
113
114
typedef char Char;
115
116
#endif
117
118
119
#define CHAR(c) ((Char)((c)&M_ASCII))
120
#define META(c) ((Char)((c)|M_QUOTE))
121
#define M_ALL META('*')
122
#define M_END META(']')
123
#define M_NOT META('!')
124
#define M_ONE META('?')
125
#define M_RNG META('-')
126
#define M_SET META('[')
127
#define M_CLASS META(':')
128
#define ismeta(c) (((c)&M_QUOTE) != 0)
129
130
#define GLOB_LIMIT_MALLOC 65536
131
#define GLOB_LIMIT_STAT 2048
132
#define GLOB_LIMIT_READDIR 16384
133
134
struct glob_lim {
135
size_t glim_malloc;
136
size_t glim_stat;
137
size_t glim_readdir;
138
};
139
140
static int compare(const void *, const void *);
141
static int g_Ctoc(const Char * restrict, char * restrict, size_t);
142
static int g_lstat(Char * restrict, struct stat * restrict, glob_t *restrict);
143
static DIR *g_opendir(Char *, glob_t *);
144
static Char *g_strchr(const Char *, int);
145
static int g_strncmp(const Char *, const char *, size_t);
146
static int g_stat(Char * restrict, struct stat * restrict, glob_t * restrict);
147
static int glob0(const Char * restrict, glob_t * restrict, struct glob_lim * restrict);
148
static int glob1(Char *, Char *, glob_t * restrict, struct glob_lim * restrict);
149
static int glob2(Char *, Char *, Char *, Char *, Char *, Char *,
150
glob_t * restrict, struct glob_lim * restrict);
151
static int glob3(Char *, Char *, Char *, Char *, Char *,
152
Char *, Char *, glob_t * restrict, struct glob_lim * restrict);
153
static int globextend(const Char * restrict, glob_t * restrict, struct glob_lim * restrict,
154
struct stat * restrict);
155
static const Char *
156
globtilde(const Char * restrict, Char * restrict, size_t, glob_t * restrict);
157
static int globexp1(const Char * restrict, glob_t * restrict, struct glob_lim * restrict);
158
static int globexp2(const Char *, const Char *, glob_t * restrict,
159
struct glob_lim * restrict);
160
static int match(Char *, Char *, Char *);
161
#ifdef DEBUG
162
static void qprintf(const char * restrict, Char * restrict);
163
#endif
164
165
int
166
sudo_glob(const char * restrict pattern, int flags, int (*errfunc)(const char *, int),
167
glob_t * restrict pglob)
168
{
169
const unsigned char *patnext;
170
int c;
171
Char *bufnext, *bufend, patbuf[PATH_MAX];
172
struct glob_lim limit = { 0, 0, 0 };
173
174
patnext = (unsigned char *) pattern;
175
if (!(flags & GLOB_APPEND)) {
176
pglob->gl_pathc = 0;
177
pglob->gl_pathv = NULL;
178
if (!(flags & GLOB_DOOFFS))
179
pglob->gl_offs = 0;
180
}
181
pglob->gl_flags = flags & ~GLOB_MAGCHAR;
182
pglob->gl_errfunc = errfunc;
183
pglob->gl_matchc = 0;
184
185
if (pglob->gl_offs >= SSIZE_MAX || pglob->gl_pathc >= SSIZE_MAX ||
186
pglob->gl_pathc >= SSIZE_MAX - pglob->gl_offs - 1)
187
return GLOB_NOSPACE;
188
189
if (strnlen(pattern, PATH_MAX) == PATH_MAX)
190
return GLOB_NOMATCH;
191
192
bufnext = patbuf;
193
bufend = bufnext + PATH_MAX - 1;
194
if (flags & GLOB_NOESCAPE)
195
while (bufnext < bufend && (c = *patnext++) != EOS)
196
*bufnext++ = c;
197
else {
198
/* Protect the quoted characters. */
199
while (bufnext < bufend && (c = *patnext++) != EOS)
200
if (c == QUOTE) {
201
if ((c = *patnext++) == EOS) {
202
c = QUOTE;
203
--patnext;
204
}
205
*bufnext++ = c | M_PROTECT;
206
} else
207
*bufnext++ = c;
208
}
209
*bufnext = EOS;
210
211
if (flags & GLOB_BRACE)
212
return globexp1(patbuf, pglob, &limit);
213
else
214
return glob0(patbuf, pglob, &limit);
215
}
216
217
/*
218
* Expand recursively a glob {} pattern. When there is no more expansion
219
* invoke the standard globbing routine to glob the rest of the magic
220
* characters
221
*/
222
static int
223
globexp1(const Char * restrict pattern, glob_t * restrict pglob, struct glob_lim * restrict limitp)
224
{
225
const Char* ptr = pattern;
226
227
/* Protect a single {}, for find(1), like csh */
228
if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS)
229
return glob0(pattern, pglob, limitp);
230
231
if ((ptr = (const Char *) g_strchr(ptr, LBRACE)) != NULL)
232
return globexp2(ptr, pattern, pglob, limitp);
233
234
return glob0(pattern, pglob, limitp);
235
}
236
237
238
/*
239
* Recursive brace globbing helper. Tries to expand a single brace.
240
* If it succeeds then it invokes globexp1 with the new pattern.
241
* If it fails then it tries to glob the rest of the pattern and returns.
242
*/
243
static int
244
globexp2(const Char *ptr, const Char *pattern, glob_t * restrict pglob,
245
struct glob_lim * restrict limitp)
246
{
247
size_t i;
248
int rv;
249
Char *lm, *ls;
250
const Char *pe, *pm, *pl;
251
Char patbuf[PATH_MAX];
252
253
/* copy part up to the brace */
254
for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++)
255
continue;
256
*lm = EOS;
257
ls = lm;
258
259
/* Find the balanced brace */
260
for (i = 0, pe = ++ptr; *pe; pe++)
261
if (*pe == LBRACKET) {
262
/* Ignore everything between [] */
263
for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++)
264
continue;
265
if (*pe == EOS) {
266
/*
267
* We could not find a matching RBRACKET.
268
* Ignore and just look for RBRACE
269
*/
270
pe = pm;
271
}
272
} else if (*pe == LBRACE)
273
i++;
274
else if (*pe == RBRACE) {
275
if (i == 0)
276
break;
277
i--;
278
}
279
280
/* Non matching braces; just glob the pattern */
281
if (i != 0 || *pe == EOS)
282
return glob0(patbuf, pglob, limitp);
283
284
for (i = 0, pl = pm = ptr; pm <= pe; pm++) {
285
switch (*pm) {
286
case LBRACKET:
287
/* Ignore everything between [] */
288
for (pl = pm++; *pm != RBRACKET && *pm != EOS; pm++)
289
continue;
290
if (*pm == EOS) {
291
/*
292
* We could not find a matching RBRACKET.
293
* Ignore and just look for RBRACE
294
*/
295
pm = pl;
296
}
297
break;
298
299
case LBRACE:
300
i++;
301
break;
302
303
case RBRACE:
304
if (i) {
305
i--;
306
break;
307
}
308
FALLTHROUGH;
309
case COMMA:
310
if (i && *pm == COMMA)
311
break;
312
else {
313
/* Append the current string */
314
for (lm = ls; (pl < pm); *lm++ = *pl++)
315
continue;
316
317
/*
318
* Append the rest of the pattern after the
319
* closing brace
320
*/
321
for (pl = pe + 1; (*lm++ = *pl++) != EOS; )
322
continue;
323
324
/* Expand the current pattern */
325
#ifdef DEBUG
326
qprintf("globexp2:", patbuf);
327
#endif
328
rv = globexp1(patbuf, pglob, limitp);
329
if (rv && rv != GLOB_NOMATCH)
330
return rv;
331
332
/* move after the comma, to the next string */
333
pl = pm + 1;
334
}
335
break;
336
337
default:
338
break;
339
}
340
}
341
return 0;
342
}
343
344
345
346
/*
347
* expand tilde from the passwd file.
348
*/
349
static const Char *
350
globtilde(const Char * restrict pattern, Char * restrict patbuf, size_t patbuf_len, glob_t * restrict pglob)
351
{
352
struct passwd *pwd;
353
char *h;
354
const Char *p;
355
Char *b, *eb;
356
357
if (*pattern != TILDE || !(pglob->gl_flags & GLOB_TILDE))
358
return pattern;
359
360
/* Copy up to the end of the string or / */
361
eb = &patbuf[patbuf_len - 1];
362
for (p = pattern + 1, h = (char *) patbuf;
363
h < (char *)eb && *p && *p != SLASH; *h++ = *p++)
364
continue;
365
366
*h = EOS;
367
368
if (((char *) patbuf)[0] == EOS) {
369
/*
370
* handle a plain ~ or ~/ by expanding $HOME
371
* first and then trying the password file
372
*/
373
if ((h = getenv("HOME")) == NULL) {
374
if ((pwd = getpwuid(getuid())) == NULL)
375
return pattern;
376
else
377
h = pwd->pw_dir;
378
}
379
} else {
380
/*
381
* Expand a ~user
382
*/
383
if ((pwd = getpwnam((char*) patbuf)) == NULL)
384
return pattern;
385
else
386
h = pwd->pw_dir;
387
}
388
389
/* Copy the home directory */
390
for (b = patbuf; b < eb && *h; *b++ = *h++)
391
continue;
392
393
/* Append the rest of the pattern */
394
while (b < eb && (*b++ = *p++) != EOS)
395
continue;
396
*b = EOS;
397
398
return patbuf;
399
}
400
401
static int
402
g_strncmp(const Char *s1, const char *s2, size_t n)
403
{
404
int rv = 0;
405
406
while (n--) {
407
rv = *(Char *)s1 - *(const unsigned char *)s2++;
408
if (rv)
409
break;
410
if (*s1++ == '\0')
411
break;
412
}
413
return rv;
414
}
415
416
static int
417
g_charclass(const Char ** restrict patternp, Char ** restrict bufnextp)
418
{
419
const Char *pattern = *patternp + 1;
420
Char *bufnext = *bufnextp;
421
const Char *colon;
422
struct cclass *cc;
423
size_t len;
424
425
if ((colon = g_strchr(pattern, ':')) == NULL || colon[1] != ']')
426
return 1; /* not a character class */
427
428
len = (size_t)(colon - pattern);
429
for (cc = cclasses; cc->name != NULL; cc++) {
430
if (!g_strncmp(pattern, cc->name, len) && cc->name[len] == '\0')
431
break;
432
}
433
if (cc->name == NULL)
434
return -1; /* invalid character class */
435
*bufnext++ = M_CLASS;
436
*bufnext++ = (Char)(cc - &cclasses[0]);
437
*bufnextp = bufnext;
438
*patternp += len + 3;
439
440
return 0;
441
}
442
443
/*
444
* The main glob() routine: compiles the pattern (optionally processing
445
* quotes), calls glob1() to do the real pattern matching, and finally
446
* sorts the list (unless unsorted operation is requested). Returns 0
447
* if things went well, nonzero if errors occurred. It is not an error
448
* to find no matches.
449
*/
450
static int
451
glob0(const Char * restrict pattern, glob_t * restrict pglob, struct glob_lim * restrict limitp)
452
{
453
const Char *qpatnext;
454
int c, err;
455
size_t oldpathc;
456
Char *bufnext, patbuf[PATH_MAX];
457
458
qpatnext = globtilde(pattern, patbuf, PATH_MAX, pglob);
459
oldpathc = pglob->gl_pathc;
460
bufnext = patbuf;
461
462
/* We don't need to check for buffer overflow any more. */
463
while ((c = *qpatnext++) != EOS) {
464
switch (c) {
465
case LBRACKET:
466
c = *qpatnext;
467
if (c == NOT)
468
++qpatnext;
469
if (*qpatnext == EOS ||
470
g_strchr(qpatnext+1, RBRACKET) == NULL) {
471
*bufnext++ = LBRACKET;
472
if (c == NOT)
473
--qpatnext;
474
break;
475
}
476
*bufnext++ = M_SET;
477
if (c == NOT)
478
*bufnext++ = M_NOT;
479
c = *qpatnext++;
480
do {
481
if (c == LBRACKET && *qpatnext == ':') {
482
do {
483
err = g_charclass(&qpatnext,
484
&bufnext);
485
if (err)
486
break;
487
c = *qpatnext++;
488
} while (c == LBRACKET && *qpatnext == ':');
489
if (err == -1 &&
490
!(pglob->gl_flags & GLOB_NOCHECK))
491
return GLOB_NOMATCH;
492
if (c == RBRACKET)
493
break;
494
}
495
*bufnext++ = CHAR(c);
496
if (*qpatnext == RANGE &&
497
(c = qpatnext[1]) != RBRACKET) {
498
*bufnext++ = M_RNG;
499
*bufnext++ = CHAR(c);
500
qpatnext += 2;
501
}
502
} while ((c = *qpatnext++) != RBRACKET);
503
pglob->gl_flags |= GLOB_MAGCHAR;
504
*bufnext++ = M_END;
505
break;
506
case QUESTION:
507
pglob->gl_flags |= GLOB_MAGCHAR;
508
*bufnext++ = M_ONE;
509
break;
510
case STAR:
511
pglob->gl_flags |= GLOB_MAGCHAR;
512
/* collapse adjacent stars to one,
513
* to avoid exponential behavior
514
*/
515
if (bufnext == patbuf || bufnext[-1] != M_ALL)
516
*bufnext++ = M_ALL;
517
break;
518
default:
519
*bufnext++ = CHAR(c);
520
break;
521
}
522
}
523
*bufnext = EOS;
524
#ifdef DEBUG
525
qprintf("glob0:", patbuf);
526
#endif
527
528
if ((err = glob1(patbuf, patbuf + PATH_MAX - 1, pglob, limitp)) != 0)
529
return err;
530
531
/*
532
* If there was no match we are going to append the pattern
533
* if GLOB_NOCHECK was specified.
534
*/
535
if (pglob->gl_pathc == oldpathc) {
536
if ((pglob->gl_flags & GLOB_NOCHECK))
537
return globextend(pattern, pglob, limitp, NULL);
538
else
539
return GLOB_NOMATCH;
540
}
541
if (!(pglob->gl_flags & GLOB_NOSORT)) {
542
qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,
543
pglob->gl_pathc - oldpathc, sizeof(char *), compare);
544
}
545
return 0;
546
}
547
548
static int
549
compare(const void *p, const void *q)
550
{
551
return strcmp(*(char **)p, *(char **)q);
552
}
553
554
static int
555
glob1(Char *pattern, Char *pattern_last, glob_t * restrict pglob, struct glob_lim * restrict limitp)
556
{
557
Char pathbuf[PATH_MAX];
558
559
/* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
560
if (*pattern == EOS)
561
return 0;
562
return glob2(pathbuf, pathbuf + PATH_MAX - 1,
563
pathbuf, pathbuf + PATH_MAX - 1,
564
pattern, pattern_last, pglob, limitp);
565
}
566
567
/*
568
* The functions glob2 and glob3 are mutually recursive; there is one level
569
* of recursion for each segment in the pattern that contains one or more
570
* meta characters.
571
*/
572
static int
573
glob2(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last,
574
Char *pattern, Char *pattern_last, glob_t * restrict pglob, struct glob_lim * restrict limitp)
575
{
576
struct stat sb;
577
Char *p, *q;
578
int anymeta;
579
580
/*
581
* Loop over pattern segments until end of pattern or until
582
* segment with meta character found.
583
*/
584
for (anymeta = 0;;) {
585
if (*pattern == EOS) { /* End of pattern? */
586
*pathend = EOS;
587
588
if ((pglob->gl_flags & GLOB_LIMIT) &&
589
limitp->glim_stat++ >= GLOB_LIMIT_STAT) {
590
errno = 0;
591
*pathend++ = SEP;
592
*pathend = EOS;
593
return GLOB_NOSPACE;
594
}
595
if (g_lstat(pathbuf, &sb, pglob))
596
return 0;
597
598
if (((pglob->gl_flags & GLOB_MARK) &&
599
pathend[-1] != SEP) && (S_ISDIR(sb.st_mode) ||
600
(S_ISLNK(sb.st_mode) &&
601
(g_stat(pathbuf, &sb, pglob) == 0) &&
602
S_ISDIR(sb.st_mode)))) {
603
if (pathend+1 > pathend_last)
604
return 1;
605
*pathend++ = SEP;
606
*pathend = EOS;
607
}
608
++pglob->gl_matchc;
609
return globextend(pathbuf, pglob, limitp, &sb);
610
}
611
612
/* Find end of next segment, copy tentatively to pathend. */
613
q = pathend;
614
p = pattern;
615
while (*p != EOS && *p != SEP) {
616
if (ismeta(*p))
617
anymeta = 1;
618
if (q+1 > pathend_last)
619
return 1;
620
*q++ = *p++;
621
}
622
623
if (!anymeta) { /* No expansion, do next segment. */
624
pathend = q;
625
pattern = p;
626
while (*pattern == SEP) {
627
if (pathend+1 > pathend_last)
628
return 1;
629
*pathend++ = *pattern++;
630
}
631
} else
632
/* Need expansion, recurse. */
633
return glob3(pathbuf, pathbuf_last, pathend,
634
pathend_last, pattern, p, pattern_last,
635
pglob, limitp);
636
}
637
/* NOTREACHED */
638
}
639
640
static int
641
glob3(Char *pathbuf, Char *pathbuf_last, Char *pathend, Char *pathend_last,
642
Char *pattern, Char *restpattern, Char *restpattern_last, glob_t * restrict pglob,
643
struct glob_lim * restrict limitp)
644
{
645
struct dirent *dp;
646
DIR *dirp;
647
int err;
648
char buf[PATH_MAX];
649
650
if (pathend > pathend_last)
651
return 1;
652
*pathend = EOS;
653
errno = 0;
654
655
if ((dirp = g_opendir(pathbuf, pglob)) == NULL) {
656
/* TODO: don't call for ENOENT or ENOTDIR? */
657
if (pglob->gl_errfunc) {
658
if (g_Ctoc(pathbuf, buf, sizeof(buf)))
659
return GLOB_ABORTED;
660
if (pglob->gl_errfunc(buf, errno) ||
661
pglob->gl_flags & GLOB_ERR)
662
return GLOB_ABORTED;
663
}
664
return 0;
665
}
666
667
err = 0;
668
669
/* Search directory for matching names. */
670
while ((dp = readdir(dirp))) {
671
unsigned char *sc;
672
Char *dc;
673
674
if ((pglob->gl_flags & GLOB_LIMIT) &&
675
limitp->glim_readdir++ >= GLOB_LIMIT_READDIR) {
676
errno = 0;
677
*pathend++ = SEP;
678
*pathend = EOS;
679
err = GLOB_NOSPACE;
680
break;
681
}
682
683
/* Initial DOT must be matched literally. */
684
if (dp->d_name[0] == DOT && *pattern != DOT)
685
continue;
686
dc = pathend;
687
sc = (unsigned char *) dp->d_name;
688
while (dc < pathend_last && (*dc++ = *sc++) != EOS)
689
continue;
690
if (dc >= pathend_last) {
691
*dc = EOS;
692
err = 1;
693
break;
694
}
695
696
if (!match(pathend, pattern, restpattern)) {
697
*pathend = EOS;
698
continue;
699
}
700
err = glob2(pathbuf, pathbuf_last, --dc, pathend_last,
701
restpattern, restpattern_last, pglob, limitp);
702
if (err)
703
break;
704
}
705
706
closedir(dirp);
707
return err;
708
}
709
710
/*
711
* Extend the gl_pathv member of a glob_t structure to accommodate a new item,
712
* add the new item, and update gl_pathc.
713
*
714
* This assumes the BSD realloc, which only copies the block when its size
715
* crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
716
* behavior.
717
*
718
* Return 0 if new item added, error code if memory couldn't be allocated.
719
*
720
* Invariant of the glob_t structure:
721
* Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
722
* gl_pathv points to (gl_offs + gl_pathc + 1) items.
723
*/
724
static int
725
globextend(const Char * restrict path, glob_t * restrict pglob, struct glob_lim * restrict limitp,
726
struct stat * restrict sb)
727
{
728
char **pathv;
729
size_t i, newn, len;
730
char *copy = NULL;
731
const Char *p;
732
733
newn = 2 + pglob->gl_pathc + pglob->gl_offs;
734
if (pglob->gl_offs >= SSIZE_MAX ||
735
pglob->gl_pathc >= SSIZE_MAX ||
736
newn >= SSIZE_MAX ||
737
SIZE_MAX / sizeof(*pathv) <= newn) {
738
nospace:
739
for (i = pglob->gl_offs; i < newn - 2; i++) {
740
if (pglob->gl_pathv && pglob->gl_pathv[i])
741
free(pglob->gl_pathv[i]);
742
}
743
if (pglob->gl_pathv) {
744
free(pglob->gl_pathv);
745
pglob->gl_pathv = NULL;
746
}
747
return GLOB_NOSPACE;
748
}
749
750
pathv = reallocarray(pglob->gl_pathv, newn, sizeof(*pathv));
751
if (pathv == NULL)
752
goto nospace;
753
if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
754
/* first time around -- clear initial gl_offs items */
755
pathv += pglob->gl_offs;
756
for (i = pglob->gl_offs; i > 0; i--)
757
*--pathv = NULL;
758
}
759
pglob->gl_pathv = pathv;
760
761
for (p = path; *p++;)
762
continue;
763
len = (size_t)(p - path);
764
limitp->glim_malloc += len;
765
if ((copy = malloc(len)) != NULL) {
766
if (g_Ctoc(path, copy, len)) {
767
free(copy);
768
return GLOB_NOSPACE;
769
}
770
pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
771
}
772
pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
773
774
if ((pglob->gl_flags & GLOB_LIMIT) &&
775
(newn * sizeof(*pathv)) + limitp->glim_malloc >
776
GLOB_LIMIT_MALLOC) {
777
errno = 0;
778
return GLOB_NOSPACE;
779
}
780
return copy == NULL ? GLOB_NOSPACE : 0;
781
}
782
783
784
/*
785
* pattern matching function for filenames. Each occurrence of the *
786
* pattern causes an iteration.
787
*
788
* Note, this function differs from the original as per the discussion
789
* here: https://research.swtch.com/glob
790
*
791
* Basically we removed the recursion and made it use the algorithm
792
* from Russ Cox to not go quadratic on cases like a file called
793
* ("a" x 100) . "x" matched against a pattern like "a*a*a*a*a*a*a*y".
794
*/
795
static int
796
match(Char *name, Char *pat, Char *patend)
797
{
798
int ok, negate_range;
799
Char c, k;
800
Char *nextp = NULL;
801
Char *nextn = NULL;
802
803
loop:
804
while (pat < patend) {
805
c = *pat++;
806
switch (c & M_MASK) {
807
case M_ALL:
808
while (pat < patend && (*pat & M_MASK) == M_ALL)
809
pat++; /* eat consecutive '*' */
810
if (pat == patend)
811
return 1;
812
if (*name == EOS)
813
return 0;
814
nextn = name + 1;
815
nextp = pat - 1;
816
break;
817
case M_ONE:
818
if (*name++ == EOS)
819
goto fail;
820
break;
821
case M_SET:
822
ok = 0;
823
if ((k = *name++) == EOS)
824
goto fail;
825
if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS)
826
++pat;
827
while (((c = *pat++) & M_MASK) != M_END) {
828
if ((c & M_MASK) == M_CLASS) {
829
Char idx = *pat & M_MASK;
830
if (idx < NCCLASSES &&
831
cclasses[idx].isctype(k))
832
ok = 1;
833
++pat;
834
}
835
if ((*pat & M_MASK) == M_RNG) {
836
if (c <= k && k <= pat[1])
837
ok = 1;
838
pat += 2;
839
} else if (c == k)
840
ok = 1;
841
}
842
if (ok == negate_range)
843
goto fail;
844
break;
845
default:
846
if (*name++ != c)
847
goto fail;
848
break;
849
}
850
}
851
if (*name == EOS)
852
return 1;
853
fail:
854
if (nextn) {
855
pat = nextp;
856
name = nextn;
857
goto loop;
858
}
859
return 0;
860
}
861
862
/* Free allocated data belonging to a glob_t structure. */
863
void
864
sudo_globfree(glob_t *pglob)
865
{
866
size_t i;
867
char **pp;
868
869
if (pglob->gl_pathv != NULL) {
870
pp = pglob->gl_pathv + pglob->gl_offs;
871
for (i = pglob->gl_pathc; i--; ++pp)
872
if (*pp)
873
free(*pp);
874
free(pglob->gl_pathv);
875
pglob->gl_pathv = NULL;
876
}
877
}
878
879
static DIR *
880
g_opendir(Char *str, glob_t *pglob)
881
{
882
char buf[PATH_MAX];
883
884
if (!*str) {
885
buf[0] = '.';
886
buf[1] = '\0';
887
} else {
888
if (g_Ctoc(str, buf, sizeof(buf)))
889
return NULL;
890
}
891
892
return opendir(buf);
893
}
894
895
static int
896
g_lstat(Char * restrict fn, struct stat * restrict sb, glob_t * restrict pglob)
897
{
898
char buf[PATH_MAX];
899
900
if (g_Ctoc(fn, buf, sizeof(buf)))
901
return -1;
902
return lstat(buf, sb);
903
}
904
905
static int
906
g_stat(Char * restrict fn, struct stat * restrict sb, glob_t * restrict pglob)
907
{
908
char buf[PATH_MAX];
909
910
if (g_Ctoc(fn, buf, sizeof(buf)))
911
return -1;
912
return stat(buf, sb);
913
}
914
915
static Char *
916
g_strchr(const Char *str, int ch)
917
{
918
do {
919
if (*str == ch)
920
return (Char *)str;
921
} while (*str++);
922
return NULL;
923
}
924
925
static int
926
g_Ctoc(const Char * restrict str, char * restrict buf, size_t len)
927
{
928
929
while (len--) {
930
if ((*buf++ = *str++) == EOS)
931
return 0;
932
}
933
return 1;
934
}
935
936
#ifdef DEBUG
937
static void
938
qprintf(const char * restrict str, Char * restrict s)
939
{
940
Char *p;
941
942
(void)printf("%s:\n", str);
943
for (p = s; *p; p++)
944
(void)putchar(CHAR(*p));
945
(void)putchar('\n');
946
for (p = s; *p; p++)
947
(void)putchar(*p & M_PROTECT ? '"' : ' ');
948
(void)putchar('\n');
949
for (p = s; *p; p++)
950
(void)putchar(ismeta(*p) ? '_' : ' ');
951
(void)putchar('\n');
952
}
953
#endif /* DEBUG */
954
#endif /* HAVE_GLOB */
955
956