aboutsummaryrefslogtreecommitdiffstats
path: root/lib/argv_split.c
blob: 1e9a6cbc3689827b2739fb3ac23d2b4927a1ced1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/*
 * Helper function for splitting a string into an argv-like array.
 */

#include <linux/kernel.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/export.h>

static const char *skip_arg(const char *cp)
{
	while (*cp && !isspace(*cp))
		cp++;

	return cp;
}

static int count_argc(const char *str)
{
	int count = 0;

	while (*str) {
		str = skip_spaces(str);
		if (*str) {
			count++;
			str = skip_arg(str);
		}
	}

	return count;
}

/**
 * argv_free - free an argv
 * @argv - the argument vector to be freed
 *
 * Frees an argv and the strings it points to.
 */
void argv_free(char **argv)
{
	char **p;
	for (p = argv; *p; p++)
		kfree(*p);

	kfree(argv);
}
EXPORT_SYMBOL(argv_free);

/**
 * argv_split - split a string at whitespace, returning an argv
 * @gfp: the GFP mask used to allocate memory
 * @str: the string to be split
 * @argcp: returned argument count
 *
 * Returns an array of pointers to strings which are split out from
 * @str.  This is performed by strictly splitting on white-space; no
 * quote processing is performed.  Multiple whitespace characters are
 * considered to be a single argument separator.  The returned array
 * is always NULL-terminated.  Returns NULL on memory allocation
 * failure.
 */
char **argv_split(gfp_t gfp, const char *str, int *argcp)
{
	int argc = count_argc(str);
	char **argv = kzalloc(sizeof(*argv) * (argc+1), gfp);
	char **argvp;

	if (argv == NULL)
		goto out;

	if (argcp)
		*argcp = argc;

	argvp = argv;

	while (*str) {
		str = skip_spaces(str);

		if (*str) {
			const char *p = str;
			char *t;

			str = skip_arg(str);

			t = kstrndup(p, str-p, gfp);
			if (t == NULL)
				goto fail;
			*argvp++ = t;
		}
	}
	*argvp = NULL;

  out:
	return argv;

  fail:
	argv_free(argv);
	return NULL;
}
EXPORT_SYMBOL(argv_split);
gt;obj == obj && shadow->id == id; } /** * klp_shadow_get() - retrieve a shadow variable data pointer * @obj: pointer to parent object * @id: data identifier * * Return: the shadow variable data element, NULL on failure. */ void *klp_shadow_get(void *obj, unsigned long id) { struct klp_shadow *shadow; rcu_read_lock(); hash_for_each_possible_rcu(klp_shadow_hash, shadow, node, (unsigned long)obj) { if (klp_shadow_match(shadow, obj, id)) { rcu_read_unlock(); return shadow->data; } } rcu_read_unlock(); return NULL; } EXPORT_SYMBOL_GPL(klp_shadow_get); static void *__klp_shadow_get_or_alloc(void *obj, unsigned long id, size_t size, gfp_t gfp_flags, klp_shadow_ctor_t ctor, void *ctor_data, bool warn_on_exist) { struct klp_shadow *new_shadow; void *shadow_data; unsigned long flags; /* Check if the shadow variable already exists */ shadow_data = klp_shadow_get(obj, id); if (shadow_data) goto exists; /* * Allocate a new shadow variable. Fill it with zeroes by default. * More complex setting can be done by @ctor function. But it is * called only when the buffer is really used (under klp_shadow_lock). */ new_shadow = kzalloc(size + sizeof(*new_shadow), gfp_flags); if (!new_shadow) return NULL; /* Look for <obj, id> again under the lock */ spin_lock_irqsave(&klp_shadow_lock, flags); shadow_data = klp_shadow_get(obj, id); if (unlikely(shadow_data)) { /* * Shadow variable was found, throw away speculative * allocation. */ spin_unlock_irqrestore(&klp_shadow_lock, flags); kfree(new_shadow); goto exists; } new_shadow->obj = obj; new_shadow->id = id; if (ctor) { int err; err = ctor(obj, new_shadow->data, ctor_data); if (err) { spin_unlock_irqrestore(&klp_shadow_lock, flags); kfree(new_shadow); pr_err("Failed to construct shadow variable <%p, %lx> (%d)\n", obj, id, err); return NULL; } } /* No <obj, id> found, so attach the newly allocated one */ hash_add_rcu(klp_shadow_hash, &new_shadow->node, (unsigned long)new_shadow->obj); spin_unlock_irqrestore(&klp_shadow_lock, flags); return new_shadow->data; exists: if (warn_on_exist) { WARN(1, "Duplicate shadow variable <%p, %lx>\n", obj, id); return NULL; } return shadow_data; } /** * klp_shadow_alloc() - allocate and add a new shadow variable * @obj: pointer to parent object * @id: data identifier * @size: size of attached data * @gfp_flags: GFP mask for allocation * @ctor: custom constructor to initialize the shadow data (optional) * @ctor_data: pointer to any data needed by @ctor (optional) * * Allocates @size bytes for new shadow variable data using @gfp_flags. * The data are zeroed by default. They are further initialized by @ctor * function if it is not NULL. The new shadow variable is then added * to the global hashtable. * * If an existing <obj, id> shadow variable can be found, this routine will * issue a WARN, exit early and return NULL. * * This function guarantees that the constructor function is called only when * the variable did not exist before. The cost is that @ctor is called * in atomic context under a spin lock. * * Return: the shadow variable data element, NULL on duplicate or * failure. */ void *klp_shadow_alloc(void *obj, unsigned long id, size_t size, gfp_t gfp_flags, klp_shadow_ctor_t ctor, void *ctor_data) { return __klp_shadow_get_or_alloc(obj, id, size, gfp_flags, ctor, ctor_data, true); } EXPORT_SYMBOL_GPL(klp_shadow_alloc); /** * klp_shadow_get_or_alloc() - get existing or allocate a new shadow variable * @obj: pointer to parent object * @id: data identifier * @size: size of attached data * @gfp_flags: GFP mask for allocation * @ctor: custom constructor to initialize the shadow data (optional) * @ctor_data: pointer to any data needed by @ctor (optional) * * Returns a pointer to existing shadow data if an <obj, id> shadow * variable is already present. Otherwise, it creates a new shadow * variable like klp_shadow_alloc(). * * This function guarantees that only one shadow variable exists with the given * @id for the given @obj. It also guarantees that the constructor function * will be called only when the variable did not exist before. The cost is * that @ctor is called in atomic context under a spin lock. * * Return: the shadow variable data element, NULL on failure. */ void *klp_shadow_get_or_alloc(void *obj, unsigned long id, size_t size, gfp_t gfp_flags, klp_shadow_ctor_t ctor, void *ctor_data) { return __klp_shadow_get_or_alloc(obj, id, size, gfp_flags, ctor, ctor_data, false); } EXPORT_SYMBOL_GPL(klp_shadow_get_or_alloc); static void klp_shadow_free_struct(struct klp_shadow *shadow, klp_shadow_dtor_t dtor) { hash_del_rcu(&shadow->node); if (dtor) dtor(shadow->obj, shadow->data); kfree_rcu(shadow, rcu_head); } /** * klp_shadow_free() - detach and free a <obj, id> shadow variable * @obj: pointer to parent object * @id: data identifier * @dtor: custom callback that can be used to unregister the variable * and/or free data that the shadow variable points to (optional) * * This function releases the memory for this <obj, id> shadow variable * instance, callers should stop referencing it accordingly. */ void klp_shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor) { struct klp_shadow *shadow; unsigned long flags; spin_lock_irqsave(&klp_shadow_lock, flags); /* Delete <obj, id> from hash */ hash_for_each_possible(klp_shadow_hash, shadow, node, (unsigned long)obj) { if (klp_shadow_match(shadow, obj, id)) { klp_shadow_free_struct(shadow, dtor); break; } } spin_unlock_irqrestore(&klp_shadow_lock, flags); } EXPORT_SYMBOL_GPL(klp_shadow_free); /** * klp_shadow_free_all() - detach and free all <*, id> shadow variables * @id: data identifier * @dtor: custom callback that can be used to unregister the variable * and/or free data that the shadow variable points to (optional) * * This function releases the memory for all <*, id> shadow variable * instances, callers should stop referencing them accordingly. */ void klp_shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor) { struct klp_shadow *shadow; unsigned long flags; int i; spin_lock_irqsave(&klp_shadow_lock, flags); /* Delete all <*, id> from hash */ hash_for_each(klp_shadow_hash, i, shadow, node) { if (klp_shadow_match(shadow, shadow->obj, id)) klp_shadow_free_struct(shadow, dtor); } spin_unlock_irqrestore(&klp_shadow_lock, flags); } EXPORT_SYMBOL_GPL(klp_shadow_free_all);