1*d9a51c35Sjmc /* $OpenBSD: rules.c,v 1.5 2022/12/26 19:16:02 jmc Exp $ */ 22157a8e5Sclaudio /* 32157a8e5Sclaudio * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org> 42157a8e5Sclaudio * 52157a8e5Sclaudio * Permission to use, copy, modify, and distribute this software for any 62157a8e5Sclaudio * purpose with or without fee is hereby granted, provided that the above 72157a8e5Sclaudio * copyright notice and this permission notice appear in all copies. 82157a8e5Sclaudio * 92157a8e5Sclaudio * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 102157a8e5Sclaudio * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 112157a8e5Sclaudio * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 122157a8e5Sclaudio * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 132157a8e5Sclaudio * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 142157a8e5Sclaudio * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 152157a8e5Sclaudio * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 162157a8e5Sclaudio */ 1757987d16Sclaudio #include <err.h> 1857987d16Sclaudio #include <stdlib.h> 1957987d16Sclaudio #include <stdio.h> 2057987d16Sclaudio #include <string.h> 2157987d16Sclaudio 2257987d16Sclaudio #include "extern.h" 2357987d16Sclaudio 2457987d16Sclaudio struct rule { 2557987d16Sclaudio char *pattern; 2657987d16Sclaudio enum rule_type type; 2757987d16Sclaudio #ifdef NOTYET 2857987d16Sclaudio unsigned int modifiers; 2957987d16Sclaudio #endif 3057987d16Sclaudio short numseg; 3157987d16Sclaudio unsigned char anchored; 3257987d16Sclaudio unsigned char fileonly; 3357987d16Sclaudio unsigned char nowild; 3457987d16Sclaudio unsigned char onlydir; 3557987d16Sclaudio unsigned char leadingdir; 3657987d16Sclaudio }; 3757987d16Sclaudio 3857987d16Sclaudio static struct rule *rules; 3957987d16Sclaudio static size_t numrules; /* number of rules */ 4057987d16Sclaudio static size_t rulesz; /* available size */ 4157987d16Sclaudio 4257987d16Sclaudio /* up to protocol 29 filter rules only support - + ! and no modifiers */ 4357987d16Sclaudio 4457987d16Sclaudio const struct command { 4557987d16Sclaudio enum rule_type type; 4657987d16Sclaudio char sopt; 4757987d16Sclaudio const char *lopt; 4857987d16Sclaudio } commands[] = { 4957987d16Sclaudio { RULE_EXCLUDE, '-', "exclude" }, 5057987d16Sclaudio { RULE_INCLUDE, '+', "include" }, 5157987d16Sclaudio { RULE_CLEAR, '!', "clear" }, 5257987d16Sclaudio #ifdef NOTYET 5357987d16Sclaudio { RULE_MERGE, '.', "merge" }, 5457987d16Sclaudio { RULE_DIR_MERGE, ':', "dir-merge" }, 5557987d16Sclaudio { RULE_SHOW, 'S', "show" }, 5657987d16Sclaudio { RULE_HIDE, 'H', "hide" }, 5757987d16Sclaudio { RULE_PROTECT, 'P', "protect" }, 5857987d16Sclaudio { RULE_RISK, 'R', "risk" }, 5957987d16Sclaudio #endif 6057987d16Sclaudio { 0 } 6157987d16Sclaudio }; 6257987d16Sclaudio 6357987d16Sclaudio #ifdef NOTYET 6457987d16Sclaudio #define MOD_ABSOLUTE 0x0001 6557987d16Sclaudio #define MOD_NEGATE 0x0002 6657987d16Sclaudio #define MOD_CVSEXCLUDE 0x0004 6757987d16Sclaudio #define MOD_SENDING 0x0008 6857987d16Sclaudio #define MOD_RECEIVING 0x0010 6957987d16Sclaudio #define MOD_PERISHABLE 0x0020 7057987d16Sclaudio #define MOD_XATTR 0x0040 7157987d16Sclaudio #define MOD_MERGE_EXCLUDE 0x0080 7257987d16Sclaudio #define MOD_MERGE_INCLUDE 0x0100 7357987d16Sclaudio #define MOD_MERGE_CVSCOMPAT 0x0200 7457987d16Sclaudio #define MOD_MERGE_EXCLUDE_FILE 0x0400 7557987d16Sclaudio #define MOD_MERGE_NO_INHERIT 0x0800 7657987d16Sclaudio #define MOD_MERGE_WORDSPLIT 0x1000 7757987d16Sclaudio 7857987d16Sclaudio /* maybe support absolute and negate */ 7957987d16Sclaudio const struct modifier { 8057987d16Sclaudio unsigned int modifier; 8157987d16Sclaudio char sopt; 8257987d16Sclaudio } modifiers[] = { 8357987d16Sclaudio { MOD_ABSOLUTE, '/' }, 8457987d16Sclaudio { MOD_NEGATE, '!' }, 8557987d16Sclaudio { MOD_CVSEXCLUDE, 'C' }, 8657987d16Sclaudio { MOD_SENDING, 's' }, 8757987d16Sclaudio { MOD_RECEIVING, 'r' }, 8857987d16Sclaudio { MOD_PERISHABLE, 'p' }, 8957987d16Sclaudio { MOD_XATTR, 'x' }, 9057987d16Sclaudio /* for '.' and ':' types */ 9157987d16Sclaudio { MOD_MERGE_EXCLUDE, '-' }, 9257987d16Sclaudio { MOD_MERGE_INCLUDE, '+' }, 9357987d16Sclaudio { MOD_MERGE_CVSCOMPAT, 'C' }, 9457987d16Sclaudio { MOD_MERGE_EXCLUDE_FILE, 'e' }, 9557987d16Sclaudio { MOD_MERGE_NO_INHERIT, 'n' }, 9657987d16Sclaudio { MOD_MERGE_WORDSPLIT, 'w' }, 9757987d16Sclaudio { 0 } 9857987d16Sclaudio } 9957987d16Sclaudio #endif 10057987d16Sclaudio 10157987d16Sclaudio static struct rule * 10257987d16Sclaudio get_next_rule(void) 10357987d16Sclaudio { 10457987d16Sclaudio struct rule *new; 10557987d16Sclaudio size_t newsz; 10657987d16Sclaudio 10757987d16Sclaudio if (++numrules > rulesz) { 10857987d16Sclaudio if (rulesz == 0) 10957987d16Sclaudio newsz = 16; 11057987d16Sclaudio else 11157987d16Sclaudio newsz = rulesz * 2; 11257987d16Sclaudio 11357987d16Sclaudio new = recallocarray(rules, rulesz, newsz, sizeof(*rules)); 11457987d16Sclaudio if (new == NULL) 11557987d16Sclaudio err(ERR_NOMEM, NULL); 11657987d16Sclaudio 11757987d16Sclaudio rules = new; 11857987d16Sclaudio rulesz = newsz; 11957987d16Sclaudio } 12057987d16Sclaudio 12157987d16Sclaudio return rules + numrules - 1; 12257987d16Sclaudio } 12357987d16Sclaudio 12457987d16Sclaudio static enum rule_type 12557987d16Sclaudio parse_command(const char *command, size_t len) 12657987d16Sclaudio { 12757987d16Sclaudio const char *mod; 12857987d16Sclaudio size_t i; 12957987d16Sclaudio 13057987d16Sclaudio mod = memchr(command, ',', len); 13157987d16Sclaudio if (mod != NULL) { 13257987d16Sclaudio /* XXX modifiers not yet implemented */ 13357987d16Sclaudio return RULE_NONE; 13457987d16Sclaudio } 13557987d16Sclaudio 13657987d16Sclaudio for (i = 0; commands[i].type != RULE_NONE; i++) { 13757987d16Sclaudio if (strncmp(commands[i].lopt, command, len) == 0) 13857987d16Sclaudio return commands[i].type; 13957987d16Sclaudio if (len == 1 && commands[i].sopt == *command) 14057987d16Sclaudio return commands[i].type; 14157987d16Sclaudio } 14257987d16Sclaudio 14357987d16Sclaudio return RULE_NONE; 14457987d16Sclaudio } 14557987d16Sclaudio 14657987d16Sclaudio static void 14757987d16Sclaudio parse_pattern(struct rule *r, char *pattern) 14857987d16Sclaudio { 14957987d16Sclaudio size_t plen; 15057987d16Sclaudio char *p; 15157987d16Sclaudio short nseg = 1; 15257987d16Sclaudio 15357987d16Sclaudio /* 15457987d16Sclaudio * check for / at start and end of pattern both are special and 15557987d16Sclaudio * can bypass full path matching. 15657987d16Sclaudio */ 15757987d16Sclaudio if (*pattern == '/') { 15857987d16Sclaudio pattern++; 15957987d16Sclaudio r->anchored = 1; 16057987d16Sclaudio } 16157987d16Sclaudio plen = strlen(pattern); 16257987d16Sclaudio /* 16357987d16Sclaudio * check for patterns ending in '/' and '/'+'***' and handle them 16457987d16Sclaudio * specially. Because of this and the check above pattern will never 16557987d16Sclaudio * start or end with a '/'. 16657987d16Sclaudio */ 16757987d16Sclaudio if (plen > 1 && pattern[plen - 1] == '/') { 16857987d16Sclaudio r->onlydir = 1; 16957987d16Sclaudio pattern[plen - 1] = '\0'; 17057987d16Sclaudio } 17157987d16Sclaudio if (plen > 4 && strcmp(pattern + plen - 4, "/***") == 0) { 17257987d16Sclaudio r->leadingdir = 1; 17357987d16Sclaudio pattern[plen - 4] = '\0'; 17457987d16Sclaudio } 17557987d16Sclaudio 17657987d16Sclaudio /* count how many segments the pattern has. */ 17757987d16Sclaudio for (p = pattern; *p != '\0'; p++) 17857987d16Sclaudio if (*p == '/') 17957987d16Sclaudio nseg++; 18057987d16Sclaudio r->numseg = nseg; 18157987d16Sclaudio 18257987d16Sclaudio /* check if this pattern only matches against the basename */ 18357987d16Sclaudio if (nseg == 1 && !r->anchored) 18457987d16Sclaudio r->fileonly = 1; 18557987d16Sclaudio 18657987d16Sclaudio if (strpbrk(pattern, "*?[") == NULL) { 18757987d16Sclaudio /* no wildchar matching */ 18857987d16Sclaudio r->nowild = 1; 18957987d16Sclaudio } else { 19057987d16Sclaudio /* requires wildchar matching */ 19157987d16Sclaudio if (strstr(pattern, "**") != NULL) 19257987d16Sclaudio r->numseg = -1; 19357987d16Sclaudio } 19457987d16Sclaudio 19557987d16Sclaudio r->pattern = strdup(pattern); 19657987d16Sclaudio if (r->pattern == NULL) 19757987d16Sclaudio err(ERR_NOMEM, NULL); 19857987d16Sclaudio } 19957987d16Sclaudio 20057987d16Sclaudio int 20157987d16Sclaudio parse_rule(char *line, enum rule_type def) 20257987d16Sclaudio { 20357987d16Sclaudio enum rule_type type; 20457987d16Sclaudio struct rule *r; 20557987d16Sclaudio char *pattern; 20657987d16Sclaudio size_t len; 20757987d16Sclaudio 20857987d16Sclaudio switch (*line) { 20957987d16Sclaudio case '#': 21057987d16Sclaudio case ';': 21157987d16Sclaudio /* comment */ 21257987d16Sclaudio return 0; 21357987d16Sclaudio case '\0': 214*d9a51c35Sjmc /* ignore empty lines */ 21557987d16Sclaudio return 0; 21657987d16Sclaudio default: 21757987d16Sclaudio len = strcspn(line, " _"); 21857987d16Sclaudio type = parse_command(line, len); 21957987d16Sclaudio if (type == RULE_NONE) { 22057987d16Sclaudio if (def == RULE_NONE) 22157987d16Sclaudio return -1; 22257987d16Sclaudio type = def; 22357987d16Sclaudio pattern = line; 22457987d16Sclaudio } else 22557987d16Sclaudio pattern = line + len + 1; 22657987d16Sclaudio 22757987d16Sclaudio if (*pattern == '\0' && type != RULE_CLEAR) 22857987d16Sclaudio return -1; 22957987d16Sclaudio if (*pattern != '\0' && type == RULE_CLEAR) 23057987d16Sclaudio return -1; 23157987d16Sclaudio break; 23257987d16Sclaudio } 23357987d16Sclaudio 23457987d16Sclaudio r = get_next_rule(); 23557987d16Sclaudio r->type = type; 23657987d16Sclaudio parse_pattern(r, pattern); 23757987d16Sclaudio 23857987d16Sclaudio return 0; 23957987d16Sclaudio } 24057987d16Sclaudio 24157987d16Sclaudio void 2420345af14Sclaudio parse_file(const char *file, enum rule_type def) 24357987d16Sclaudio { 24457987d16Sclaudio FILE *fp; 24557987d16Sclaudio char *line = NULL; 24657987d16Sclaudio size_t linesize = 0, linenum = 0; 24757987d16Sclaudio ssize_t linelen; 24857987d16Sclaudio 24957987d16Sclaudio if ((fp = fopen(file, "r")) == NULL) 25057987d16Sclaudio err(ERR_SYNTAX, "open: %s", file); 25157987d16Sclaudio 2520345af14Sclaudio while ((linelen = getline(&line, &linesize, fp)) != -1) { 25357987d16Sclaudio linenum++; 25457987d16Sclaudio line[linelen - 1] = '\0'; 25557987d16Sclaudio if (parse_rule(line, def) == -1) 25657987d16Sclaudio errx(ERR_SYNTAX, "syntax error in %s at entry %zu", 25757987d16Sclaudio file, linenum); 25857987d16Sclaudio } 25957987d16Sclaudio 26057987d16Sclaudio free(line); 26157987d16Sclaudio if (ferror(fp)) 26257987d16Sclaudio err(ERR_SYNTAX, "failed to parse file %s", file); 26357987d16Sclaudio fclose(fp); 26457987d16Sclaudio } 26557987d16Sclaudio 26657987d16Sclaudio static const char * 26757987d16Sclaudio send_command(struct rule *r) 26857987d16Sclaudio { 26957987d16Sclaudio static char buf[16]; 27057987d16Sclaudio char *b = buf; 27157987d16Sclaudio char *ep = buf + sizeof(buf); 27257987d16Sclaudio 27357987d16Sclaudio switch (r->type) { 27457987d16Sclaudio case RULE_EXCLUDE: 27557987d16Sclaudio *b++ = '-'; 27657987d16Sclaudio break; 27757987d16Sclaudio case RULE_INCLUDE: 27857987d16Sclaudio *b++ = '+'; 27957987d16Sclaudio break; 28057987d16Sclaudio case RULE_CLEAR: 28157987d16Sclaudio *b++ = '!'; 28257987d16Sclaudio break; 28357987d16Sclaudio #ifdef NOTYET 28457987d16Sclaudio case RULE_MERGE: 28557987d16Sclaudio *b++ = '.'; 28657987d16Sclaudio break; 28757987d16Sclaudio case RULE_DIR_MERGE: 28857987d16Sclaudio *b++ = ':'; 28957987d16Sclaudio break; 29057987d16Sclaudio case RULE_SHOW: 29157987d16Sclaudio *b++ = 'S'; 29257987d16Sclaudio break; 29357987d16Sclaudio case RULE_HIDE: 29457987d16Sclaudio *b++ = 'H'; 29557987d16Sclaudio break; 29657987d16Sclaudio case RULE_PROTECT: 29757987d16Sclaudio *b++ = 'P'; 29857987d16Sclaudio break; 29957987d16Sclaudio case RULE_RISK: 30057987d16Sclaudio *b++ = 'R'; 30157987d16Sclaudio break; 30257987d16Sclaudio #endif 30357987d16Sclaudio default: 30457987d16Sclaudio err(ERR_SYNTAX, "unknown rule type %d", r->type); 30557987d16Sclaudio } 30657987d16Sclaudio 30757987d16Sclaudio #ifdef NOTYET 30857987d16Sclaudio for (i = 0; modifiers[i].modifier != 0; i++) { 30957987d16Sclaudio if (rule->modifiers & modifiers[i].modifier) 31057987d16Sclaudio *b++ = modifiers[i].sopt; 31157987d16Sclaudio if (b >= ep - 3) 31257987d16Sclaudio err(ERR_SYNTAX, "rule modifiers overflow"); 31357987d16Sclaudio } 31457987d16Sclaudio #endif 31557987d16Sclaudio if (b >= ep - 3) 31657987d16Sclaudio err(ERR_SYNTAX, "rule prefix overflow"); 31757987d16Sclaudio *b++ = ' '; 31857987d16Sclaudio 31957987d16Sclaudio /* include the stripped root '/' for anchored patterns */ 32057987d16Sclaudio if (r->anchored) 32157987d16Sclaudio *b++ = '/'; 32257987d16Sclaudio *b++ = '\0'; 32357987d16Sclaudio return buf; 32457987d16Sclaudio } 32557987d16Sclaudio 32657987d16Sclaudio static const char * 32757987d16Sclaudio postfix_command(struct rule *r) 32857987d16Sclaudio { 32957987d16Sclaudio static char buf[8]; 33057987d16Sclaudio 33157987d16Sclaudio buf[0] = '\0'; 33257987d16Sclaudio if (r->onlydir) 33357987d16Sclaudio strlcpy(buf, "/", sizeof(buf)); 33457987d16Sclaudio if (r->leadingdir) 33557987d16Sclaudio strlcpy(buf, "/***", sizeof(buf)); 33657987d16Sclaudio 33757987d16Sclaudio return buf; 33857987d16Sclaudio } 33957987d16Sclaudio 34057987d16Sclaudio void 34157987d16Sclaudio send_rules(struct sess *sess, int fd) 34257987d16Sclaudio { 34357987d16Sclaudio const char *cmd; 34457987d16Sclaudio const char *postfix; 34557987d16Sclaudio struct rule *r; 34657987d16Sclaudio size_t cmdlen, len, postlen, i; 34757987d16Sclaudio 34857987d16Sclaudio for (i = 0; i < numrules; i++) { 34957987d16Sclaudio r = &rules[i]; 35057987d16Sclaudio cmd = send_command(r); 35157987d16Sclaudio if (cmd == NULL) 35257987d16Sclaudio err(ERR_PROTOCOL, 35357987d16Sclaudio "rules are incompatible with remote rsync"); 35457987d16Sclaudio postfix = postfix_command(r); 35557987d16Sclaudio cmdlen = strlen(cmd); 35657987d16Sclaudio len = strlen(r->pattern); 35757987d16Sclaudio postlen = strlen(postfix); 35857987d16Sclaudio 35957987d16Sclaudio if (!io_write_int(sess, fd, cmdlen + len + postlen)) 36057987d16Sclaudio err(ERR_SOCK_IO, "send rules"); 36157987d16Sclaudio if (!io_write_buf(sess, fd, cmd, cmdlen)) 36257987d16Sclaudio err(ERR_SOCK_IO, "send rules"); 36357987d16Sclaudio if (!io_write_buf(sess, fd, r->pattern, len)) 36457987d16Sclaudio err(ERR_SOCK_IO, "send rules"); 36557987d16Sclaudio /* include the '/' stripped by onlydir */ 36657987d16Sclaudio if (postlen > 0) 36757987d16Sclaudio if (!io_write_buf(sess, fd, postfix, postlen)) 36857987d16Sclaudio err(ERR_SOCK_IO, "send rules"); 36957987d16Sclaudio } 37057987d16Sclaudio 37157987d16Sclaudio if (!io_write_int(sess, fd, 0)) 37257987d16Sclaudio err(ERR_SOCK_IO, "send rules"); 37357987d16Sclaudio } 37457987d16Sclaudio 37557987d16Sclaudio void 37657987d16Sclaudio recv_rules(struct sess *sess, int fd) 37757987d16Sclaudio { 37857987d16Sclaudio char line[8192]; 37957987d16Sclaudio size_t len; 38057987d16Sclaudio 38157987d16Sclaudio do { 38257987d16Sclaudio if (!io_read_size(sess, fd, &len)) 38357987d16Sclaudio err(ERR_SOCK_IO, "receive rules"); 38457987d16Sclaudio 38557987d16Sclaudio if (len == 0) 38657987d16Sclaudio return; 38757987d16Sclaudio if (len >= sizeof(line) - 1) 38857987d16Sclaudio errx(ERR_SOCK_IO, "received rule too long"); 38957987d16Sclaudio if (!io_read_buf(sess, fd, line, len)) 39057987d16Sclaudio err(ERR_SOCK_IO, "receive rules"); 39157987d16Sclaudio line[len] = '\0'; 39257987d16Sclaudio if (parse_rule(line, RULE_NONE) == -1) 39357987d16Sclaudio errx(ERR_PROTOCOL, "syntax error in received rules"); 39457987d16Sclaudio } while (1); 39557987d16Sclaudio } 39657987d16Sclaudio 39757987d16Sclaudio static inline int 39857987d16Sclaudio rule_matched(struct rule *r) 39957987d16Sclaudio { 40057987d16Sclaudio /* TODO apply negation once modifiers are added */ 40157987d16Sclaudio 40257987d16Sclaudio if (r->type == RULE_EXCLUDE) 40357987d16Sclaudio return -1; 40457987d16Sclaudio else 40557987d16Sclaudio return 1; 40657987d16Sclaudio } 40757987d16Sclaudio 40857987d16Sclaudio int 40957987d16Sclaudio rules_match(const char *path, int isdir) 41057987d16Sclaudio { 41157987d16Sclaudio const char *basename, *p = NULL; 41257987d16Sclaudio struct rule *r; 41357987d16Sclaudio size_t i; 41457987d16Sclaudio 41557987d16Sclaudio basename = strrchr(path, '/'); 41657987d16Sclaudio if (basename != NULL) 41757987d16Sclaudio basename += 1; 41857987d16Sclaudio else 41957987d16Sclaudio basename = path; 42057987d16Sclaudio 42157987d16Sclaudio for (i = 0; i < numrules; i++) { 42257987d16Sclaudio r = &rules[i]; 42357987d16Sclaudio 42457987d16Sclaudio if (r->onlydir && !isdir) 42557987d16Sclaudio continue; 42657987d16Sclaudio 42757987d16Sclaudio if (r->nowild) { 42857987d16Sclaudio /* fileonly and anchored are mutually exclusive */ 42957987d16Sclaudio if (r->fileonly) { 43057987d16Sclaudio if (strcmp(basename, r->pattern) == 0) 43157987d16Sclaudio return rule_matched(r); 43257987d16Sclaudio } else if (r->anchored) { 43357987d16Sclaudio /* 43457987d16Sclaudio * assumes that neither path nor pattern 43557987d16Sclaudio * start with a '/'. 43657987d16Sclaudio */ 43757987d16Sclaudio if (strcmp(path, r->pattern) == 0) 43857987d16Sclaudio return rule_matched(r); 43957987d16Sclaudio } else if (r->leadingdir) { 44057987d16Sclaudio size_t plen = strlen(r->pattern); 44157987d16Sclaudio 44257987d16Sclaudio p = strstr(path, r->pattern); 44357987d16Sclaudio /* 44457987d16Sclaudio * match from start or dir boundary also 44557987d16Sclaudio * match to end or to dir boundary 44657987d16Sclaudio */ 44757987d16Sclaudio if (p != NULL && (p == path || p[-1] == '/') && 44857987d16Sclaudio (p[plen] == '\0' || p[plen] == '/')) 44957987d16Sclaudio return rule_matched(r); 45057987d16Sclaudio } else { 45157987d16Sclaudio size_t len = strlen(path); 45257987d16Sclaudio size_t plen = strlen(r->pattern); 45357987d16Sclaudio 45457987d16Sclaudio if (len >= plen && strcmp(path + len - plen, 45557987d16Sclaudio r->pattern) == 0) { 45657987d16Sclaudio /* match all or start on dir boundary */ 45757987d16Sclaudio if (len == plen || 45857987d16Sclaudio path[len - plen - 1] == '/') 45957987d16Sclaudio return rule_matched(r); 46057987d16Sclaudio } 46157987d16Sclaudio } 46257987d16Sclaudio } else { 46357987d16Sclaudio if (r->fileonly) { 46457987d16Sclaudio p = basename; 46557987d16Sclaudio } else if (r->anchored || r->numseg == -1) { 46657987d16Sclaudio /* full path matching */ 46757987d16Sclaudio p = path; 46857987d16Sclaudio } else { 46957987d16Sclaudio short nseg = 1; 47057987d16Sclaudio 47157987d16Sclaudio /* match against the last numseg elements */ 47257987d16Sclaudio for (p = path; *p != '\0'; p++) 47357987d16Sclaudio if (*p == '/') 47457987d16Sclaudio nseg++; 47557987d16Sclaudio if (nseg < r->numseg) { 47657987d16Sclaudio p = NULL; 47757987d16Sclaudio } else { 47857987d16Sclaudio nseg -= r->numseg; 47957987d16Sclaudio for (p = path; *p != '\0' && nseg > 0; 48057987d16Sclaudio p++) { 48157987d16Sclaudio if (*p == '/') 48257987d16Sclaudio nseg--; 48357987d16Sclaudio } 48457987d16Sclaudio } 48557987d16Sclaudio } 48657987d16Sclaudio 48757987d16Sclaudio if (p != NULL) { 48857987d16Sclaudio if (rmatch(r->pattern, p, r->leadingdir) == 0) 48957987d16Sclaudio return rule_matched(r); 49057987d16Sclaudio } 49157987d16Sclaudio } 49257987d16Sclaudio } 49357987d16Sclaudio 49457987d16Sclaudio return 0; 49557987d16Sclaudio } 496