diff options
-rw-r--r-- | include/linux/textsearch.h | 180 | ||||
-rw-r--r-- | lib/Kconfig | 8 | ||||
-rw-r--r-- | lib/Makefile | 2 | ||||
-rw-r--r-- | lib/textsearch.c | 317 |
4 files changed, 506 insertions, 1 deletions
diff --git a/include/linux/textsearch.h b/include/linux/textsearch.h new file mode 100644 index 000000000000..941f45ac117a --- /dev/null +++ b/include/linux/textsearch.h | |||
@@ -0,0 +1,180 @@ | |||
1 | #ifndef __LINUX_TEXTSEARCH_H | ||
2 | #define __LINUX_TEXTSEARCH_H | ||
3 | |||
4 | #ifdef __KERNEL__ | ||
5 | |||
6 | #include <linux/types.h> | ||
7 | #include <linux/list.h> | ||
8 | #include <linux/kernel.h> | ||
9 | #include <linux/module.h> | ||
10 | #include <linux/err.h> | ||
11 | |||
12 | struct ts_config; | ||
13 | |||
14 | /** | ||
15 | * TS_AUTOLOAD - Automatically load textsearch modules when needed | ||
16 | */ | ||
17 | #define TS_AUTOLOAD 1 | ||
18 | |||
19 | /** | ||
20 | * struct ts_state - search state | ||
21 | * @offset: offset for next match | ||
22 | * @cb: control buffer, for persistant variables of get_next_block() | ||
23 | */ | ||
24 | struct ts_state | ||
25 | { | ||
26 | unsigned int offset; | ||
27 | char cb[40]; | ||
28 | }; | ||
29 | |||
30 | /** | ||
31 | * struct ts_ops - search module operations | ||
32 | * @name: name of search algorithm | ||
33 | * @init: initialization function to prepare a search | ||
34 | * @find: find the next occurrence of the pattern | ||
35 | * @destroy: destroy algorithm specific parts of a search configuration | ||
36 | * @get_pattern: return head of pattern | ||
37 | * @get_pattern_len: return length of pattern | ||
38 | * @owner: module reference to algorithm | ||
39 | */ | ||
40 | struct ts_ops | ||
41 | { | ||
42 | const char *name; | ||
43 | struct ts_config * (*init)(const void *, unsigned int, int); | ||
44 | unsigned int (*find)(struct ts_config *, | ||
45 | struct ts_state *); | ||
46 | void (*destroy)(struct ts_config *); | ||
47 | void * (*get_pattern)(struct ts_config *); | ||
48 | unsigned int (*get_pattern_len)(struct ts_config *); | ||
49 | struct module *owner; | ||
50 | struct list_head list; | ||
51 | }; | ||
52 | |||
53 | /** | ||
54 | * struct ts_config - search configuration | ||
55 | * @ops: operations of chosen algorithm | ||
56 | * @get_next_block: callback to fetch the next block to search in | ||
57 | * @finish: callback to finalize a search | ||
58 | */ | ||
59 | struct ts_config | ||
60 | { | ||
61 | struct ts_ops *ops; | ||
62 | |||
63 | /** | ||
64 | * get_next_block - fetch next block of data | ||
65 | * @consumed: number of bytes consumed by the caller | ||
66 | * @dst: destination buffer | ||
67 | * @conf: search configuration | ||
68 | * @state: search state | ||
69 | * | ||
70 | * Called repeatedly until 0 is returned. Must assign the | ||
71 | * head of the next block of data to &*dst and return the length | ||
72 | * of the block or 0 if at the end. consumed == 0 indicates | ||
73 | * a new search. May store/read persistant values in state->cb. | ||
74 | */ | ||
75 | unsigned int (*get_next_block)(unsigned int consumed, | ||
76 | const u8 **dst, | ||
77 | struct ts_config *conf, | ||
78 | struct ts_state *state); | ||
79 | |||
80 | /** | ||
81 | * finish - finalize/clean a series of get_next_block() calls | ||
82 | * @conf: search configuration | ||
83 | * @state: search state | ||
84 | * | ||
85 | * Called after the last use of get_next_block(), may be used | ||
86 | * to cleanup any leftovers. | ||
87 | */ | ||
88 | void (*finish)(struct ts_config *conf, | ||
89 | struct ts_state *state); | ||
90 | }; | ||
91 | |||
92 | /** | ||
93 | * textsearch_next - continue searching for a pattern | ||
94 | * @conf: search configuration | ||
95 | * @state: search state | ||
96 | * | ||
97 | * Continues a search looking for more occurrences of the pattern. | ||
98 | * textsearch_find() must be called to find the first occurrence | ||
99 | * in order to reset the state. | ||
100 | * | ||
101 | * Returns the position of the next occurrence of the pattern or | ||
102 | * UINT_MAX if not match was found. | ||
103 | */ | ||
104 | static inline unsigned int textsearch_next(struct ts_config *conf, | ||
105 | struct ts_state *state) | ||
106 | { | ||
107 | unsigned int ret = conf->ops->find(conf, state); | ||
108 | |||
109 | if (conf->finish) | ||
110 | conf->finish(conf, state); | ||
111 | |||
112 | return ret; | ||
113 | } | ||
114 | |||
115 | /** | ||
116 | * textsearch_find - start searching for a pattern | ||
117 | * @conf: search configuration | ||
118 | * @state: search state | ||
119 | * | ||
120 | * Returns the position of first occurrence of the pattern or | ||
121 | * UINT_MAX if no match was found. | ||
122 | */ | ||
123 | static inline unsigned int textsearch_find(struct ts_config *conf, | ||
124 | struct ts_state *state) | ||
125 | { | ||
126 | state->offset = 0; | ||
127 | return textsearch_next(conf, state); | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * textsearch_get_pattern - return head of the pattern | ||
132 | * @conf: search configuration | ||
133 | */ | ||
134 | static inline void *textsearch_get_pattern(struct ts_config *conf) | ||
135 | { | ||
136 | return conf->ops->get_pattern(conf); | ||
137 | } | ||
138 | |||
139 | /** | ||
140 | * textsearch_get_pattern_len - return length of the pattern | ||
141 | * @conf: search configuration | ||
142 | */ | ||
143 | static inline unsigned int textsearch_get_pattern_len(struct ts_config *conf) | ||
144 | { | ||
145 | return conf->ops->get_pattern_len(conf); | ||
146 | } | ||
147 | |||
148 | extern int textsearch_register(struct ts_ops *); | ||
149 | extern int textsearch_unregister(struct ts_ops *); | ||
150 | extern struct ts_config *textsearch_prepare(const char *, const void *, | ||
151 | unsigned int, int, int); | ||
152 | extern void textsearch_destroy(struct ts_config *conf); | ||
153 | extern unsigned int textsearch_find_continuous(struct ts_config *, | ||
154 | struct ts_state *, | ||
155 | const void *, unsigned int); | ||
156 | |||
157 | |||
158 | #define TS_PRIV_ALIGNTO 8 | ||
159 | #define TS_PRIV_ALIGN(len) (((len) + TS_PRIV_ALIGNTO-1) & ~(TS_PRIV_ALIGNTO-1)) | ||
160 | |||
161 | static inline struct ts_config *alloc_ts_config(size_t payload, int gfp_mask) | ||
162 | { | ||
163 | struct ts_config *conf; | ||
164 | |||
165 | conf = kmalloc(TS_PRIV_ALIGN(sizeof(*conf)) + payload, gfp_mask); | ||
166 | if (conf == NULL) | ||
167 | return ERR_PTR(-ENOMEM); | ||
168 | |||
169 | memset(conf, 0, TS_PRIV_ALIGN(sizeof(*conf)) + payload); | ||
170 | return conf; | ||
171 | } | ||
172 | |||
173 | static inline void *ts_config_priv(struct ts_config *conf) | ||
174 | { | ||
175 | return ((u8 *) conf + TS_PRIV_ALIGN(sizeof(struct ts_config))); | ||
176 | } | ||
177 | |||
178 | #endif /* __KERNEL__ */ | ||
179 | |||
180 | #endif | ||
diff --git a/lib/Kconfig b/lib/Kconfig index 2d4d4e3bc4aa..5bc2d523e6d1 100644 --- a/lib/Kconfig +++ b/lib/Kconfig | |||
@@ -63,5 +63,11 @@ config REED_SOLOMON_ENC16 | |||
63 | config REED_SOLOMON_DEC16 | 63 | config REED_SOLOMON_DEC16 |
64 | boolean | 64 | boolean |
65 | 65 | ||
66 | endmenu | 66 | config TEXTSEARCH |
67 | boolean "Textsearch infrastructure" | ||
68 | default y | ||
69 | help | ||
70 | Say Y here if you want to provide a textsearch infrastructure | ||
71 | to other subsystems. | ||
67 | 72 | ||
73 | endmenu | ||
diff --git a/lib/Makefile b/lib/Makefile index dcb4231916e2..3e917436ad60 100644 --- a/lib/Makefile +++ b/lib/Makefile | |||
@@ -36,6 +36,8 @@ obj-$(CONFIG_ZLIB_INFLATE) += zlib_inflate/ | |||
36 | obj-$(CONFIG_ZLIB_DEFLATE) += zlib_deflate/ | 36 | obj-$(CONFIG_ZLIB_DEFLATE) += zlib_deflate/ |
37 | obj-$(CONFIG_REED_SOLOMON) += reed_solomon/ | 37 | obj-$(CONFIG_REED_SOLOMON) += reed_solomon/ |
38 | 38 | ||
39 | lib-$(CONFIG_TEXTSEARCH) += textsearch.o | ||
40 | |||
39 | hostprogs-y := gen_crc32table | 41 | hostprogs-y := gen_crc32table |
40 | clean-files := crc32table.h | 42 | clean-files := crc32table.h |
41 | 43 | ||
diff --git a/lib/textsearch.c b/lib/textsearch.c new file mode 100644 index 000000000000..1e934c196f0f --- /dev/null +++ b/lib/textsearch.c | |||
@@ -0,0 +1,317 @@ | |||
1 | /* | ||
2 | * lib/textsearch.c Generic text search interface | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version | ||
7 | * 2 of the License, or (at your option) any later version. | ||
8 | * | ||
9 | * Authors: Thomas Graf <tgraf@suug.ch> | ||
10 | * Pablo Neira Ayuso <pablo@eurodev.net> | ||
11 | * | ||
12 | * ========================================================================== | ||
13 | * | ||
14 | * INTRODUCTION | ||
15 | * | ||
16 | * The textsearch infrastructure provides text searching facitilies for | ||
17 | * both linear and non-linear data. Individual search algorithms are | ||
18 | * implemented in modules and chosen by the user. | ||
19 | * | ||
20 | * ARCHITECTURE | ||
21 | * | ||
22 | * User | ||
23 | * +----------------+ | ||
24 | * | finish()|<--------------(6)-----------------+ | ||
25 | * |get_next_block()|<--------------(5)---------------+ | | ||
26 | * | | Algorithm | | | ||
27 | * | | +------------------------------+ | ||
28 | * | | | init() find() destroy() | | ||
29 | * | | +------------------------------+ | ||
30 | * | | Core API ^ ^ ^ | ||
31 | * | | +---------------+ (2) (4) (8) | ||
32 | * | (1)|----->| prepare() |---+ | | | ||
33 | * | (3)|----->| find()/next() |-----------+ | | ||
34 | * | (7)|----->| destroy() |----------------------+ | ||
35 | * +----------------+ +---------------+ | ||
36 | * | ||
37 | * (1) User configures a search by calling _prepare() specifying the | ||
38 | * search parameters such as the pattern and algorithm name. | ||
39 | * (2) Core requests the algorithm to allocate and initialize a search | ||
40 | * configuration according to the specified parameters. | ||
41 | * (3) User starts the search(es) by calling _find() or _next() to | ||
42 | * fetch subsequent occurrences. A state variable is provided | ||
43 | * to the algorihtm to store persistant variables. | ||
44 | * (4) Core eventually resets the search offset and forwards the find() | ||
45 | * request to the algorithm. | ||
46 | * (5) Algorithm calls get_next_block() provided by the user continously | ||
47 | * to fetch the data to be searched in block by block. | ||
48 | * (6) Algorithm invokes finish() after the last call to get_next_block | ||
49 | * to clean up any leftovers from get_next_block. (Optional) | ||
50 | * (7) User destroys the configuration by calling _destroy(). | ||
51 | * (8) Core notifies the algorithm to destroy algorithm specific | ||
52 | * allocations. (Optional) | ||
53 | * | ||
54 | * USAGE | ||
55 | * | ||
56 | * Before a search can be performed, a configuration must be created | ||
57 | * by calling textsearch_prepare() specyfing the searching algorithm and | ||
58 | * the pattern to look for. The returned configuration may then be used | ||
59 | * for an arbitary amount of times and even in parallel as long as a | ||
60 | * separate struct ts_state variable is provided to every instance. | ||
61 | * | ||
62 | * The actual search is performed by either calling textsearch_find_- | ||
63 | * continuous() for linear data or by providing an own get_next_block() | ||
64 | * implementation and calling textsearch_find(). Both functions return | ||
65 | * the position of the first occurrence of the patern or UINT_MAX if | ||
66 | * no match was found. Subsequent occurences can be found by calling | ||
67 | * textsearch_next() regardless of the linearity of the data. | ||
68 | * | ||
69 | * Once you're done using a configuration it must be given back via | ||
70 | * textsearch_destroy. | ||
71 | * | ||
72 | * EXAMPLE | ||
73 | * | ||
74 | * int pos; | ||
75 | * struct ts_config *conf; | ||
76 | * struct ts_state state; | ||
77 | * const char *pattern = "chicken"; | ||
78 | * const char *example = "We dance the funky chicken"; | ||
79 | * | ||
80 | * conf = textsearch_prepare("kmp", pattern, strlen(pattern), | ||
81 | * GFP_KERNEL, TS_AUTOLOAD); | ||
82 | * if (IS_ERR(conf)) { | ||
83 | * err = PTR_ERR(conf); | ||
84 | * goto errout; | ||
85 | * } | ||
86 | * | ||
87 | * pos = textsearch_find_continuous(conf, &state, example, strlen(example)); | ||
88 | * if (pos != UINT_MAX) | ||
89 | * panic("Oh my god, dancing chickens at %d\n", pos); | ||
90 | * | ||
91 | * textsearch_destroy(conf); | ||
92 | * | ||
93 | * ========================================================================== | ||
94 | */ | ||
95 | |||
96 | #include <linux/config.h> | ||
97 | #include <linux/module.h> | ||
98 | #include <linux/types.h> | ||
99 | #include <linux/string.h> | ||
100 | #include <linux/init.h> | ||
101 | #include <linux/rcupdate.h> | ||
102 | #include <linux/err.h> | ||
103 | #include <linux/textsearch.h> | ||
104 | |||
105 | static LIST_HEAD(ts_ops); | ||
106 | static DEFINE_SPINLOCK(ts_mod_lock); | ||
107 | |||
108 | static inline struct ts_ops *lookup_ts_algo(const char *name) | ||
109 | { | ||
110 | struct ts_ops *o; | ||
111 | |||
112 | rcu_read_lock(); | ||
113 | list_for_each_entry_rcu(o, &ts_ops, list) { | ||
114 | if (!strcmp(name, o->name)) { | ||
115 | if (!try_module_get(o->owner)) | ||
116 | o = NULL; | ||
117 | rcu_read_unlock(); | ||
118 | return o; | ||
119 | } | ||
120 | } | ||
121 | rcu_read_unlock(); | ||
122 | |||
123 | return NULL; | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * textsearch_register - register a textsearch module | ||
128 | * @ops: operations lookup table | ||
129 | * | ||
130 | * This function must be called by textsearch modules to announce | ||
131 | * their presence. The specified &@ops must have %name set to a | ||
132 | * unique identifier and the callbacks find(), init(), get_pattern(), | ||
133 | * and get_pattern_len() must be implemented. | ||
134 | * | ||
135 | * Returns 0 or -EEXISTS if another module has already registered | ||
136 | * with same name. | ||
137 | */ | ||
138 | int textsearch_register(struct ts_ops *ops) | ||
139 | { | ||
140 | int err = -EEXIST; | ||
141 | struct ts_ops *o; | ||
142 | |||
143 | if (ops->name == NULL || ops->find == NULL || ops->init == NULL || | ||
144 | ops->get_pattern == NULL || ops->get_pattern_len == NULL) | ||
145 | return -EINVAL; | ||
146 | |||
147 | spin_lock(&ts_mod_lock); | ||
148 | list_for_each_entry(o, &ts_ops, list) { | ||
149 | if (!strcmp(ops->name, o->name)) | ||
150 | goto errout; | ||
151 | } | ||
152 | |||
153 | list_add_tail_rcu(&ops->list, &ts_ops); | ||
154 | err = 0; | ||
155 | errout: | ||
156 | spin_unlock(&ts_mod_lock); | ||
157 | return err; | ||
158 | } | ||
159 | |||
160 | /** | ||
161 | * textsearch_unregister - unregister a textsearch module | ||
162 | * @ops: operations lookup table | ||
163 | * | ||
164 | * This function must be called by textsearch modules to announce | ||
165 | * their disappearance for examples when the module gets unloaded. | ||
166 | * The &ops parameter must be the same as the one during the | ||
167 | * registration. | ||
168 | * | ||
169 | * Returns 0 on success or -ENOENT if no matching textsearch | ||
170 | * registration was found. | ||
171 | */ | ||
172 | int textsearch_unregister(struct ts_ops *ops) | ||
173 | { | ||
174 | int err = 0; | ||
175 | struct ts_ops *o; | ||
176 | |||
177 | spin_lock(&ts_mod_lock); | ||
178 | list_for_each_entry(o, &ts_ops, list) { | ||
179 | if (o == ops) { | ||
180 | list_del_rcu(&o->list); | ||
181 | goto out; | ||
182 | } | ||
183 | } | ||
184 | |||
185 | err = -ENOENT; | ||
186 | out: | ||
187 | spin_unlock(&ts_mod_lock); | ||
188 | return err; | ||
189 | } | ||
190 | |||
191 | struct ts_linear_state | ||
192 | { | ||
193 | unsigned int len; | ||
194 | const void *data; | ||
195 | }; | ||
196 | |||
197 | static unsigned int get_linear_data(unsigned int consumed, const u8 **dst, | ||
198 | struct ts_config *conf, | ||
199 | struct ts_state *state) | ||
200 | { | ||
201 | struct ts_linear_state *st = (struct ts_linear_state *) state->cb; | ||
202 | |||
203 | if (likely(consumed < st->len)) { | ||
204 | *dst = st->data + consumed; | ||
205 | return st->len - consumed; | ||
206 | } | ||
207 | |||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | /** | ||
212 | * textsearch_find_continuous - search a pattern in continuous/linear data | ||
213 | * @conf: search configuration | ||
214 | * @state: search state | ||
215 | * @data: data to search in | ||
216 | * @len: length of data | ||
217 | * | ||
218 | * A simplified version of textsearch_find() for continuous/linear data. | ||
219 | * Call textsearch_next() to retrieve subsequent matches. | ||
220 | * | ||
221 | * Returns the position of first occurrence of the pattern or | ||
222 | * UINT_MAX if no occurrence was found. | ||
223 | */ | ||
224 | unsigned int textsearch_find_continuous(struct ts_config *conf, | ||
225 | struct ts_state *state, | ||
226 | const void *data, unsigned int len) | ||
227 | { | ||
228 | struct ts_linear_state *st = (struct ts_linear_state *) state->cb; | ||
229 | |||
230 | conf->get_next_block = get_linear_data; | ||
231 | st->data = data; | ||
232 | st->len = len; | ||
233 | |||
234 | return textsearch_find(conf, state); | ||
235 | } | ||
236 | |||
237 | /** | ||
238 | * textsearch_prepare - Prepare a search | ||
239 | * @algo: name of search algorithm | ||
240 | * @pattern: pattern data | ||
241 | * @len: length of pattern | ||
242 | * @gfp_mask: allocation mask | ||
243 | * @flags: search flags | ||
244 | * | ||
245 | * Looks up the search algorithm module and creates a new textsearch | ||
246 | * configuration for the specified pattern. Upon completion all | ||
247 | * necessary refcnts are held and the configuration must be put back | ||
248 | * using textsearch_put() after usage. | ||
249 | * | ||
250 | * Note: The format of the pattern may not be compatible between | ||
251 | * the various search algorithms. | ||
252 | * | ||
253 | * Returns a new textsearch configuration according to the specified | ||
254 | * parameters or a ERR_PTR(). | ||
255 | */ | ||
256 | struct ts_config *textsearch_prepare(const char *algo, const void *pattern, | ||
257 | unsigned int len, int gfp_mask, int flags) | ||
258 | { | ||
259 | int err = -ENOENT; | ||
260 | struct ts_config *conf; | ||
261 | struct ts_ops *ops; | ||
262 | |||
263 | ops = lookup_ts_algo(algo); | ||
264 | #ifdef CONFIG_KMOD | ||
265 | /* | ||
266 | * Why not always autoload you may ask. Some users are | ||
267 | * in a situation where requesting a module may deadlock, | ||
268 | * especially when the module is located on a NFS mount. | ||
269 | */ | ||
270 | if (ops == NULL && flags & TS_AUTOLOAD) { | ||
271 | request_module("ts_%s", algo); | ||
272 | ops = lookup_ts_algo(algo); | ||
273 | } | ||
274 | #endif | ||
275 | |||
276 | if (ops == NULL) | ||
277 | goto errout; | ||
278 | |||
279 | conf = ops->init(pattern, len, gfp_mask); | ||
280 | if (IS_ERR(conf)) { | ||
281 | err = PTR_ERR(conf); | ||
282 | goto errout; | ||
283 | } | ||
284 | |||
285 | conf->ops = ops; | ||
286 | return conf; | ||
287 | |||
288 | errout: | ||
289 | if (ops) | ||
290 | module_put(ops->owner); | ||
291 | |||
292 | return ERR_PTR(err); | ||
293 | } | ||
294 | |||
295 | /** | ||
296 | * textsearch_destroy - destroy a search configuration | ||
297 | * @conf: search configuration | ||
298 | * | ||
299 | * Releases all references of the configuration and frees | ||
300 | * up the memory. | ||
301 | */ | ||
302 | void textsearch_destroy(struct ts_config *conf) | ||
303 | { | ||
304 | if (conf->ops) { | ||
305 | if (conf->ops->destroy) | ||
306 | conf->ops->destroy(conf); | ||
307 | module_put(conf->ops->owner); | ||
308 | } | ||
309 | |||
310 | kfree(conf); | ||
311 | } | ||
312 | |||
313 | EXPORT_SYMBOL(textsearch_register); | ||
314 | EXPORT_SYMBOL(textsearch_unregister); | ||
315 | EXPORT_SYMBOL(textsearch_prepare); | ||
316 | EXPORT_SYMBOL(textsearch_find_continuous); | ||
317 | EXPORT_SYMBOL(textsearch_destroy); | ||