1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * erofs-utils/lib/exclude.c
4  *
5  * Created by Li Guifu <bluce.lee@aliyun.com>
6  */
7 #include <string.h>
8 #include <stdlib.h>
9 #include "erofs/err.h"
10 #include "erofs/list.h"
11 #include "erofs/print.h"
12 #include "erofs/exclude.h"
13 
14 #define EXCLUDE_RULE_EXACT_SIZE	offsetof(struct erofs_exclude_rule, reg)
15 #define EXCLUDE_RULE_REGEX_SIZE	sizeof(struct erofs_exclude_rule)
16 
17 static LIST_HEAD(exclude_head);
18 static LIST_HEAD(regex_exclude_head);
19 
dump_regerror(int errcode,const char * s,const regex_t * preg)20 static void dump_regerror(int errcode, const char *s, const regex_t *preg)
21 {
22 	char str[512];
23 
24 	regerror(errcode, preg, str, sizeof(str));
25 	erofs_err("invalid regex %s (%s)\n", s, str);
26 }
27 
erofs_insert_exclude(const char * s,bool is_regex)28 static struct erofs_exclude_rule *erofs_insert_exclude(const char *s,
29 						       bool is_regex)
30 {
31 	struct erofs_exclude_rule *r;
32 	int ret;
33 	struct list_head *h;
34 
35 	r = malloc(is_regex ? EXCLUDE_RULE_REGEX_SIZE :
36 			      EXCLUDE_RULE_EXACT_SIZE);
37 	if (!r)
38 		return ERR_PTR(-ENOMEM);
39 
40 	r->pattern = strdup(s);
41 	if (!r->pattern) {
42 		ret = -ENOMEM;
43 		goto err_rule;
44 	}
45 
46 	if (is_regex) {
47 		ret = regcomp(&r->reg, s, REG_EXTENDED|REG_NOSUB);
48 		if (ret) {
49 			dump_regerror(ret, s, &r->reg);
50 			goto err_rule;
51 		}
52 		h = &regex_exclude_head;
53 	} else {
54 		h = &exclude_head;
55 	}
56 
57 	list_add_tail(&r->list, h);
58 	erofs_info("insert exclude %s: %s\n",
59 		   is_regex ? "regex" : "path", s);
60 	return r;
61 
62 err_rule:
63 	if (r->pattern)
64 		free(r->pattern);
65 	free(r);
66 	return ERR_PTR(ret);
67 }
68 
erofs_cleanup_exclude_rules(void)69 void erofs_cleanup_exclude_rules(void)
70 {
71 	struct erofs_exclude_rule *r, *n;
72 	struct list_head *h;
73 
74 	h = &exclude_head;
75 	list_for_each_entry_safe(r, n, h, list) {
76 		list_del(&r->list);
77 		free(r->pattern);
78 		free(r);
79 	}
80 
81 	h = &regex_exclude_head;
82 	list_for_each_entry_safe(r, n, h, list) {
83 		list_del(&r->list);
84 		free(r->pattern);
85 		regfree(&r->reg);
86 		free(r);
87 	}
88 }
89 
erofs_parse_exclude_path(const char * args,bool is_regex)90 int erofs_parse_exclude_path(const char *args, bool is_regex)
91 {
92 	struct erofs_exclude_rule *r = erofs_insert_exclude(args, is_regex);
93 
94 	if (IS_ERR(r)) {
95 		erofs_cleanup_exclude_rules();
96 		return PTR_ERR(r);
97 	}
98 	return 0;
99 }
100 
erofs_is_exclude_path(const char * dir,const char * name)101 struct erofs_exclude_rule *erofs_is_exclude_path(const char *dir,
102 						 const char *name)
103 {
104 	char buf[PATH_MAX];
105 	const char *s;
106 	struct erofs_exclude_rule *r;
107 
108 	if (!dir) {
109 		/* no prefix */
110 		s = name;
111 	} else {
112 		sprintf(buf, "%s/%s", dir, name);
113 		s = buf;
114 	}
115 
116 	s = erofs_fspath(s);
117 	list_for_each_entry(r, &exclude_head, list) {
118 		if (!strcmp(r->pattern, s))
119 			return r;
120 	}
121 
122 	list_for_each_entry(r, &regex_exclude_head, list) {
123 		int ret = regexec(&r->reg, s, (size_t)0, NULL, 0);
124 
125 		if (!ret)
126 			return r;
127 		if (ret != REG_NOMATCH)
128 			dump_regerror(ret, s, &r->reg);
129 	}
130 	return NULL;
131 }
132 
133