| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * erofs-utils/lib/exclude.c |
| * |
| * Created by Li Guifu <bluce.lee@aliyun.com> |
| */ |
| #include <string.h> |
| #include <stdlib.h> |
| #include "erofs/err.h" |
| #include "erofs/list.h" |
| #include "erofs/print.h" |
| #include "erofs/exclude.h" |
| |
| #define EXCLUDE_RULE_EXACT_SIZE offsetof(struct erofs_exclude_rule, reg) |
| #define EXCLUDE_RULE_REGEX_SIZE sizeof(struct erofs_exclude_rule) |
| |
| static LIST_HEAD(exclude_head); |
| static LIST_HEAD(regex_exclude_head); |
| |
| static void dump_regerror(int errcode, const char *s, const regex_t *preg) |
| { |
| char str[512]; |
| |
| regerror(errcode, preg, str, sizeof(str)); |
| erofs_err("invalid regex %s (%s)\n", s, str); |
| } |
| |
| static struct erofs_exclude_rule *erofs_insert_exclude(const char *s, |
| bool is_regex) |
| { |
| struct erofs_exclude_rule *r; |
| int ret; |
| struct list_head *h; |
| |
| r = malloc(is_regex ? EXCLUDE_RULE_REGEX_SIZE : |
| EXCLUDE_RULE_EXACT_SIZE); |
| if (!r) |
| return ERR_PTR(-ENOMEM); |
| |
| r->pattern = strdup(s); |
| if (!r->pattern) { |
| ret = -ENOMEM; |
| goto err_rule; |
| } |
| |
| if (is_regex) { |
| ret = regcomp(&r->reg, s, REG_EXTENDED|REG_NOSUB); |
| if (ret) { |
| dump_regerror(ret, s, &r->reg); |
| goto err_rule; |
| } |
| h = ®ex_exclude_head; |
| } else { |
| h = &exclude_head; |
| } |
| |
| list_add_tail(&r->list, h); |
| erofs_info("insert exclude %s: %s\n", |
| is_regex ? "regex" : "path", s); |
| return r; |
| |
| err_rule: |
| if (r->pattern) |
| free(r->pattern); |
| free(r); |
| return ERR_PTR(ret); |
| } |
| |
| void erofs_cleanup_exclude_rules(void) |
| { |
| struct erofs_exclude_rule *r, *n; |
| struct list_head *h; |
| |
| h = &exclude_head; |
| list_for_each_entry_safe(r, n, h, list) { |
| list_del(&r->list); |
| free(r->pattern); |
| free(r); |
| } |
| |
| h = ®ex_exclude_head; |
| list_for_each_entry_safe(r, n, h, list) { |
| list_del(&r->list); |
| free(r->pattern); |
| regfree(&r->reg); |
| free(r); |
| } |
| } |
| |
| int erofs_parse_exclude_path(const char *args, bool is_regex) |
| { |
| struct erofs_exclude_rule *r = erofs_insert_exclude(args, is_regex); |
| |
| if (IS_ERR(r)) { |
| erofs_cleanup_exclude_rules(); |
| return PTR_ERR(r); |
| } |
| return 0; |
| } |
| |
| struct erofs_exclude_rule *erofs_is_exclude_path(const char *dir, |
| const char *name) |
| { |
| char buf[PATH_MAX]; |
| const char *s; |
| struct erofs_exclude_rule *r; |
| |
| if (!dir) { |
| /* no prefix */ |
| s = name; |
| } else { |
| sprintf(buf, "%s/%s", dir, name); |
| s = buf; |
| } |
| |
| s = erofs_fspath(s); |
| list_for_each_entry(r, &exclude_head, list) { |
| if (!strcmp(r->pattern, s)) |
| return r; |
| } |
| |
| list_for_each_entry(r, ®ex_exclude_head, list) { |
| int ret = regexec(&r->reg, s, (size_t)0, NULL, 0); |
| |
| if (!ret) |
| return r; |
| if (ret != REG_NOMATCH) |
| dump_regerror(ret, s, &r->reg); |
| } |
| return NULL; |
| } |
| |