Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-or-later 2 : : /* 3 : : * lib/textsearch.c Generic text search interface 4 : : * 5 : : * Authors: Thomas Graf <tgraf@suug.ch> 6 : : * Pablo Neira Ayuso <pablo@netfilter.org> 7 : : * 8 : : * ========================================================================== 9 : : */ 10 : : 11 : : /** 12 : : * DOC: ts_intro 13 : : * INTRODUCTION 14 : : * 15 : : * The textsearch infrastructure provides text searching facilities for 16 : : * both linear and non-linear data. Individual search algorithms are 17 : : * implemented in modules and chosen by the user. 18 : : * 19 : : * ARCHITECTURE 20 : : * 21 : : * .. code-block:: none 22 : : * 23 : : * User 24 : : * +----------------+ 25 : : * | finish()|<--------------(6)-----------------+ 26 : : * |get_next_block()|<--------------(5)---------------+ | 27 : : * | | Algorithm | | 28 : : * | | +------------------------------+ 29 : : * | | | init() find() destroy() | 30 : : * | | +------------------------------+ 31 : : * | | Core API ^ ^ ^ 32 : : * | | +---------------+ (2) (4) (8) 33 : : * | (1)|----->| prepare() |---+ | | 34 : : * | (3)|----->| find()/next() |-----------+ | 35 : : * | (7)|----->| destroy() |----------------------+ 36 : : * +----------------+ +---------------+ 37 : : * 38 : : * (1) User configures a search by calling textsearch_prepare() specifying 39 : : * the search parameters such as the pattern and algorithm name. 40 : : * (2) Core requests the algorithm to allocate and initialize a search 41 : : * configuration according to the specified parameters. 42 : : * (3) User starts the search(es) by calling textsearch_find() or 43 : : * textsearch_next() to fetch subsequent occurrences. A state variable 44 : : * is provided to the algorithm to store persistent variables. 45 : : * (4) Core eventually resets the search offset and forwards the find() 46 : : * request to the algorithm. 47 : : * (5) Algorithm calls get_next_block() provided by the user continuously 48 : : * to fetch the data to be searched in block by block. 49 : : * (6) Algorithm invokes finish() after the last call to get_next_block 50 : : * to clean up any leftovers from get_next_block. (Optional) 51 : : * (7) User destroys the configuration by calling textsearch_destroy(). 52 : : * (8) Core notifies the algorithm to destroy algorithm specific 53 : : * allocations. (Optional) 54 : : * 55 : : * USAGE 56 : : * 57 : : * Before a search can be performed, a configuration must be created 58 : : * by calling textsearch_prepare() specifying the searching algorithm, 59 : : * the pattern to look for and flags. As a flag, you can set TS_IGNORECASE 60 : : * to perform case insensitive matching. But it might slow down 61 : : * performance of algorithm, so you should use it at own your risk. 62 : : * The returned configuration may then be used for an arbitrary 63 : : * amount of times and even in parallel as long as a separate struct 64 : : * ts_state variable is provided to every instance. 65 : : * 66 : : * The actual search is performed by either calling 67 : : * textsearch_find_continuous() for linear data or by providing 68 : : * an own get_next_block() implementation and 69 : : * calling textsearch_find(). Both functions return 70 : : * the position of the first occurrence of the pattern or UINT_MAX if 71 : : * no match was found. Subsequent occurrences can be found by calling 72 : : * textsearch_next() regardless of the linearity of the data. 73 : : * 74 : : * Once you're done using a configuration it must be given back via 75 : : * textsearch_destroy. 76 : : * 77 : : * EXAMPLE:: 78 : : * 79 : : * int pos; 80 : : * struct ts_config *conf; 81 : : * struct ts_state state; 82 : : * const char *pattern = "chicken"; 83 : : * const char *example = "We dance the funky chicken"; 84 : : * 85 : : * conf = textsearch_prepare("kmp", pattern, strlen(pattern), 86 : : * GFP_KERNEL, TS_AUTOLOAD); 87 : : * if (IS_ERR(conf)) { 88 : : * err = PTR_ERR(conf); 89 : : * goto errout; 90 : : * } 91 : : * 92 : : * pos = textsearch_find_continuous(conf, &state, example, strlen(example)); 93 : : * if (pos != UINT_MAX) 94 : : * panic("Oh my god, dancing chickens at %d\n", pos); 95 : : * 96 : : * textsearch_destroy(conf); 97 : : */ 98 : : /* ========================================================================== */ 99 : : 100 : : #include <linux/module.h> 101 : : #include <linux/types.h> 102 : : #include <linux/string.h> 103 : : #include <linux/init.h> 104 : : #include <linux/rculist.h> 105 : : #include <linux/rcupdate.h> 106 : : #include <linux/err.h> 107 : : #include <linux/textsearch.h> 108 : : #include <linux/slab.h> 109 : : 110 : : static LIST_HEAD(ts_ops); 111 : : static DEFINE_SPINLOCK(ts_mod_lock); 112 : : 113 : 0 : static inline struct ts_ops *lookup_ts_algo(const char *name) 114 : : { 115 : : struct ts_ops *o; 116 : : 117 : : rcu_read_lock(); 118 : 0 : list_for_each_entry_rcu(o, &ts_ops, list) { 119 : 0 : if (!strcmp(name, o->name)) { 120 : 0 : if (!try_module_get(o->owner)) 121 : : o = NULL; 122 : : rcu_read_unlock(); 123 : 0 : return o; 124 : : } 125 : : } 126 : : rcu_read_unlock(); 127 : : 128 : 0 : return NULL; 129 : : } 130 : : 131 : : /** 132 : : * textsearch_register - register a textsearch module 133 : : * @ops: operations lookup table 134 : : * 135 : : * This function must be called by textsearch modules to announce 136 : : * their presence. The specified &@ops must have %name set to a 137 : : * unique identifier and the callbacks find(), init(), get_pattern(), 138 : : * and get_pattern_len() must be implemented. 139 : : * 140 : : * Returns 0 or -EEXISTS if another module has already registered 141 : : * with same name. 142 : : */ 143 : 0 : int textsearch_register(struct ts_ops *ops) 144 : : { 145 : : int err = -EEXIST; 146 : : struct ts_ops *o; 147 : : 148 : 0 : if (ops->name == NULL || ops->find == NULL || ops->init == NULL || 149 : 0 : ops->get_pattern == NULL || ops->get_pattern_len == NULL) 150 : : return -EINVAL; 151 : : 152 : : spin_lock(&ts_mod_lock); 153 : 0 : list_for_each_entry(o, &ts_ops, list) { 154 : 0 : if (!strcmp(ops->name, o->name)) 155 : : goto errout; 156 : : } 157 : : 158 : 0 : list_add_tail_rcu(&ops->list, &ts_ops); 159 : : err = 0; 160 : : errout: 161 : : spin_unlock(&ts_mod_lock); 162 : 0 : return err; 163 : : } 164 : : EXPORT_SYMBOL(textsearch_register); 165 : : 166 : : /** 167 : : * textsearch_unregister - unregister a textsearch module 168 : : * @ops: operations lookup table 169 : : * 170 : : * This function must be called by textsearch modules to announce 171 : : * their disappearance for examples when the module gets unloaded. 172 : : * The &ops parameter must be the same as the one during the 173 : : * registration. 174 : : * 175 : : * Returns 0 on success or -ENOENT if no matching textsearch 176 : : * registration was found. 177 : : */ 178 : 0 : int textsearch_unregister(struct ts_ops *ops) 179 : : { 180 : : int err = 0; 181 : : struct ts_ops *o; 182 : : 183 : : spin_lock(&ts_mod_lock); 184 : 0 : list_for_each_entry(o, &ts_ops, list) { 185 : 0 : if (o == ops) { 186 : : list_del_rcu(&o->list); 187 : : goto out; 188 : : } 189 : : } 190 : : 191 : : err = -ENOENT; 192 : : out: 193 : : spin_unlock(&ts_mod_lock); 194 : 0 : return err; 195 : : } 196 : : EXPORT_SYMBOL(textsearch_unregister); 197 : : 198 : : struct ts_linear_state 199 : : { 200 : : unsigned int len; 201 : : const void *data; 202 : : }; 203 : : 204 : 0 : static unsigned int get_linear_data(unsigned int consumed, const u8 **dst, 205 : : struct ts_config *conf, 206 : : struct ts_state *state) 207 : : { 208 : : struct ts_linear_state *st = (struct ts_linear_state *) state->cb; 209 : : 210 : 0 : if (likely(consumed < st->len)) { 211 : 0 : *dst = st->data + consumed; 212 : 0 : return st->len - consumed; 213 : : } 214 : : 215 : : return 0; 216 : : } 217 : : 218 : : /** 219 : : * textsearch_find_continuous - search a pattern in continuous/linear data 220 : : * @conf: search configuration 221 : : * @state: search state 222 : : * @data: data to search in 223 : : * @len: length of data 224 : : * 225 : : * A simplified version of textsearch_find() for continuous/linear data. 226 : : * Call textsearch_next() to retrieve subsequent matches. 227 : : * 228 : : * Returns the position of first occurrence of the pattern or 229 : : * %UINT_MAX if no occurrence was found. 230 : : */ 231 : 0 : unsigned int textsearch_find_continuous(struct ts_config *conf, 232 : : struct ts_state *state, 233 : : const void *data, unsigned int len) 234 : : { 235 : : struct ts_linear_state *st = (struct ts_linear_state *) state->cb; 236 : : 237 : 0 : conf->get_next_block = get_linear_data; 238 : 0 : st->data = data; 239 : 0 : st->len = len; 240 : : 241 : 0 : return textsearch_find(conf, state); 242 : : } 243 : : EXPORT_SYMBOL(textsearch_find_continuous); 244 : : 245 : : /** 246 : : * textsearch_prepare - Prepare a search 247 : : * @algo: name of search algorithm 248 : : * @pattern: pattern data 249 : : * @len: length of pattern 250 : : * @gfp_mask: allocation mask 251 : : * @flags: search flags 252 : : * 253 : : * Looks up the search algorithm module and creates a new textsearch 254 : : * configuration for the specified pattern. 255 : : * 256 : : * Note: The format of the pattern may not be compatible between 257 : : * the various search algorithms. 258 : : * 259 : : * Returns a new textsearch configuration according to the specified 260 : : * parameters or a ERR_PTR(). If a zero length pattern is passed, this 261 : : * function returns EINVAL. 262 : : */ 263 : 0 : struct ts_config *textsearch_prepare(const char *algo, const void *pattern, 264 : : unsigned int len, gfp_t gfp_mask, int flags) 265 : : { 266 : : int err = -ENOENT; 267 : : struct ts_config *conf; 268 : : struct ts_ops *ops; 269 : : 270 : 0 : if (len == 0) 271 : : return ERR_PTR(-EINVAL); 272 : : 273 : 0 : ops = lookup_ts_algo(algo); 274 : : #ifdef CONFIG_MODULES 275 : : /* 276 : : * Why not always autoload you may ask. Some users are 277 : : * in a situation where requesting a module may deadlock, 278 : : * especially when the module is located on a NFS mount. 279 : : */ 280 : 0 : if (ops == NULL && flags & TS_AUTOLOAD) { 281 : 0 : request_module("ts_%s", algo); 282 : 0 : ops = lookup_ts_algo(algo); 283 : : } 284 : : #endif 285 : : 286 : 0 : if (ops == NULL) 287 : : goto errout; 288 : : 289 : 0 : conf = ops->init(pattern, len, gfp_mask, flags); 290 : 0 : if (IS_ERR(conf)) { 291 : : err = PTR_ERR(conf); 292 : 0 : goto errout; 293 : : } 294 : : 295 : 0 : conf->ops = ops; 296 : 0 : return conf; 297 : : 298 : : errout: 299 : 0 : if (ops) 300 : 0 : module_put(ops->owner); 301 : : 302 : 0 : return ERR_PTR(err); 303 : : } 304 : : EXPORT_SYMBOL(textsearch_prepare); 305 : : 306 : : /** 307 : : * textsearch_destroy - destroy a search configuration 308 : : * @conf: search configuration 309 : : * 310 : : * Releases all references of the configuration and frees 311 : : * up the memory. 312 : : */ 313 : 0 : void textsearch_destroy(struct ts_config *conf) 314 : : { 315 : 0 : if (conf->ops) { 316 : 0 : if (conf->ops->destroy) 317 : 0 : conf->ops->destroy(conf); 318 : 0 : module_put(conf->ops->owner); 319 : : } 320 : : 321 : 0 : kfree(conf); 322 : 0 : } 323 : : EXPORT_SYMBOL(textsearch_destroy);