2
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
3
* NOVELL (All rights reserved)
5
* This program is free software; you can redistribute it and/or
6
* modify it under the terms of version 2 of the GNU General Public
7
* License published by the Free Software Foundation.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, contact Novell, Inc.
23
#include <linux/limits.h>
24
#define _(s) gettext(s)
29
#include "libapparmor_re/apparmor_re.h"
30
#include "libapparmor_re/aare_rules.h"
40
/* Filters out multiple slashes (except if the first two are slashes,
41
* that's a distinct namespace in linux) and trailing slashes.
42
* NOTE: modifies in place the contents of the path argument */
44
static void filter_slashes(char *path)
49
if (!path || (strlen(path) < 2))
54
/* special case for linux // namespace */
55
if (sptr[0] == '/' && sptr[1] == '/' && sptr[2] != '/') {
81
static pattern_t convert_aaregex_to_pcre(const char *aare, int anchor,
82
char *pcre, size_t pcre_size,
85
#define STORE(_src, _dest, _len) \
86
if ((const char*)_dest + _len > (pcre + pcre_size)){ \
87
error = e_buffer_overflow; \
89
memcpy(_dest, _src, _len); \
92
#define update_re_pos(X) if (!(*first_re_pos)) { *first_re_pos = (X); }
97
/* flag to indicate input error */
98
enum error_type error;
104
BOOL bEscape = 0; /* flag to indicate escape */
105
int ingrouping = 0; /* flag to indicate {} context */
106
int incharclass = 0; /* flag to indicate [ ] context */
109
ptype = ePatternBasic; /* assume no regex */
114
if (dfaflags & DFA_DUMP_RULE_EXPR)
115
fprintf(stderr, "aare: %s -> ", aare);
118
/* anchor beginning of regular expression */
121
while (error == e_no_error && *sptr) {
125
/* concurrent escapes are allowed now and
126
* output as two consecutive escapes so that
127
* pcre won't interpret them
128
* \\\{...\\\} will be emitted as \\\{...\\\}
129
* for pcre matching. For string matching
130
* and globbing only one escape is output
131
* this is done by stripping later
134
STORE("\\\\", dptr, 2);
138
continue; /*skip turning bEscape off */
143
/* '*' is a PCRE special character */
144
/* We store an escaped *, in case we
145
* end up using this regex buffer (i.e another
146
* non-escaped regex follows)
148
STORE("\\*", dptr, 2);
150
if ((dptr > pcre) && *(dptr - 1) == '/') {
152
// handle comment containing use
153
// of C comment characters
154
// /* /*/ and /** to describe paths
156
// modify what is emitted for * and **
157
// when used as the only path
161
// this prevents these expressions
162
// from matching directories or
164
// in these case * and ** must
165
// match at least 1 character to
166
// get a valid path element.
168
// /foo/* -> should not match /foo/
169
// /foo/*bar -> should match /foo/bar
170
// /*/foo -> should not match //foo
172
const char *s = sptr;
175
if (*s == '/' || !*s) {
176
STORE("[^/\\x00]", dptr, 8);
179
if (*(sptr + 1) == '*') {
180
/* is this the first regex form we
181
* have seen and also the end of
182
* pattern? If so, we can use
183
* optimised tail globbing rather
186
update_re_pos(sptr - aare);
187
if (*(sptr + 2) == '\0' &&
188
ptype == ePatternBasic) {
189
ptype = ePatternTailGlob;
191
ptype = ePatternRegex;
194
STORE("[^\\x00]*", dptr, 8);
197
update_re_pos(sptr - aare);
198
ptype = ePatternRegex;
199
STORE("[^/\\x00]*", dptr, 9);
200
} /* *(sptr+1) == '*' */
207
/* ? is not a PCRE regex character
208
* so no need to escape, just skip
211
STORE(sptr, dptr, 1);
213
update_re_pos(sptr - aare);
214
ptype = ePatternRegex;
215
STORE("[^/\\x00]", dptr, 8);
221
/* [ is a PCRE special character */
222
STORE("\\[", dptr, 2);
224
update_re_pos(sptr - aare);
226
ptype = ePatternRegex;
227
STORE(sptr, dptr, 1);
233
/* ] is a PCRE special character */
234
STORE("\\]", dptr, 2);
237
STORE(sptr, dptr, 1);
243
/* { is a PCRE special character */
244
STORE("\\{", dptr, 2);
247
error = e_parse_error;
248
PERROR(_("%s: Illegal open {, nesting groupings not allowed\n"),
251
update_re_pos(sptr - aare);
253
ptype = ePatternRegex;
261
/* { is a PCRE special character */
262
STORE("\\}", dptr, 2);
264
if (ingrouping <= 1) {
266
error = e_parse_error;
268
if (ingrouping == 1) {
269
PERROR(_("%s: Regex grouping error: Invalid number of items between {}\n"),
272
ingrouping = 0; /* prevent further errors */
274
} else { /* ingrouping == 0 */
275
PERROR(_("%s: Regex grouping error: Invalid close }, no matching open { detected\n"),
278
} else { /* ingrouping > 1 */
288
/* , is not a PCRE regex character
289
* so no need to escape, just skip
292
STORE(sptr, dptr, 1);
298
STORE(sptr, dptr, 1);
303
/* these are special outside of character
304
* classes but not in them */
308
STORE(sptr, dptr, 1);
310
STORE("\\", dptr, 1);
311
STORE(sptr, dptr, 1);
316
* Not a subdomain regex, but needs to be
317
* escaped as it is a pcre metacharacter which
318
* we don't want to support. We always escape
319
* these, so no need to check bEscape
326
STORE("\\", dptr, 1);
327
// fall through to default
330
STORE(sptr, dptr, 1);
332
} /* switch (*sptr) */
336
} /* while error == e_no_error && *sptr) */
338
if (ingrouping > 0 || incharclass) {
339
error = e_parse_error;
341
PERROR(_("%s: Regex grouping error: Unclosed grouping or character class, expecting close }\n"),
345
/* anchor end and terminate pattern string */
346
if ((error == e_no_error) && anchor) {
347
STORE("$" , dptr, 1);
349
if (error == e_no_error) {
352
/* check error again, as above STORE may have set it */
353
if (error != e_no_error) {
354
if (error == e_buffer_overflow) {
355
PERROR(_("%s: Internal buffer overflow detected, %d characters exceeded\n"),
359
PERROR(_("%s: Unable to parse input line '%s'\n"),
368
ptype = ePatternInvalid;
370
if (dfaflags & DFA_DUMP_RULE_EXPR)
371
fprintf(stderr, "%s\n", pcre);
376
static const char *local_name(const char *name)
380
for (t = strstr(name, "//") ; t ; t = strstr(name, "//"))
386
static int process_profile_name_xmatch(struct codomain *cod)
388
char tbuf[PATH_MAX + 3]; /* +3 for ^, $ and \0 */
392
/* don't filter_slashes for profile names */
394
name = cod->attachment;
396
name = local_name(cod->name);
397
ptype = convert_aaregex_to_pcre(name, 0, tbuf, PATH_MAX + 3,
399
if (ptype == ePatternBasic)
400
cod->xmatch_len = strlen(name);
402
if (ptype == ePatternInvalid) {
403
PERROR(_("%s: Invalid profile name '%s' - bad regular expression\n"), progname, name);
405
} else if (ptype == ePatternBasic && !(cod->altnames || cod->attachment)) {
406
/* no regex so do not set xmatch */
409
cod->xmatch_size = 0;
412
aare_ruleset_t *rule = aare_new_ruleset(0);
415
if (!aare_add_rule(rule, tbuf, 0, AA_MAY_EXEC, 0, dfaflags)) {
416
aare_delete_ruleset(rule);
420
struct alt_name *alt;
421
list_for_each(cod->altnames, alt) {
423
ptype = convert_aaregex_to_pcre(alt->name, 0,
427
if (ptype == ePatternBasic)
428
len = strlen(alt->name);
429
if (len < cod->xmatch_len)
430
cod->xmatch_len = len;
431
if (!aare_add_rule(rule, tbuf, 0, AA_MAY_EXEC, 0, dfaflags)) {
432
aare_delete_ruleset(rule);
437
cod->xmatch = aare_create_dfa(rule, &cod->xmatch_size,
439
aare_delete_ruleset(rule);
447
static int process_dfa_entry(aare_ruleset_t *dfarules, struct cod_entry *entry)
449
char tbuf[PATH_MAX + 3]; /* +3 for ^, $ and \0 */
453
if (!entry) /* shouldn't happen */
457
if (entry->mode & ~AA_CHANGE_PROFILE)
458
filter_slashes(entry->name);
459
ptype = convert_aaregex_to_pcre(entry->name, 0, tbuf, PATH_MAX+3, &pos);
460
if (ptype == ePatternInvalid)
463
entry->pattern_type = ptype;
465
/* ix implies m but the apparmor module does not add m bit to
466
* dfa states like it does for pcre
468
if ((entry->mode >> AA_OTHER_SHIFT) & AA_EXEC_INHERIT)
469
entry->mode |= AA_EXEC_MMAP << AA_OTHER_SHIFT;
470
if ((entry->mode >> AA_USER_SHIFT) & AA_EXEC_INHERIT)
471
entry->mode |= AA_EXEC_MMAP << AA_USER_SHIFT;
473
/* relying on ptrace and change_profile not getting merged earlier */
475
/* the link bit on the first pair entry should not get masked
476
* out by a deny rule, as both pieces of the link pair must
477
* match. audit info for the link is carried on the second
480
if (entry->deny && (entry->mode & AA_LINK_BITS)) {
481
if (!aare_add_rule(dfarules, tbuf, entry->deny,
482
entry->mode & ~AA_LINK_BITS,
483
entry->audit & ~AA_LINK_BITS, dfaflags))
485
} else if (entry->mode & ~AA_CHANGE_PROFILE) {
486
if (!aare_add_rule(dfarules, tbuf, entry->deny, entry->mode,
487
entry->audit, dfaflags))
491
if (entry->mode & (AA_LINK_BITS)) {
492
/* add the pair rule */
493
char lbuf[PATH_MAX + 8];
494
int perms = AA_LINK_BITS & entry->mode;
498
if (entry->link_name) {
499
ptype = convert_aaregex_to_pcre(entry->link_name, 0, lbuf, PATH_MAX + 8, &pos);
500
if (ptype == ePatternInvalid)
503
perms |= LINK_TO_LINK_SUBSET(perms);
506
perms |= LINK_TO_LINK_SUBSET(perms);
509
if (!aare_add_rule_vec(dfarules, entry->deny, perms, entry->audit & AA_LINK_BITS, 2, vec, dfaflags))
512
if (entry->mode & AA_CHANGE_PROFILE) {
513
if (entry->namespace) {
515
char lbuf[PATH_MAX + 8];
517
ptype = convert_aaregex_to_pcre(entry->namespace, 0, lbuf, PATH_MAX + 8, &pos);
520
if (!aare_add_rule_vec(dfarules, 0, AA_CHANGE_PROFILE, 0, 2, vec, dfaflags))
523
if (!aare_add_rule(dfarules, tbuf, 0, AA_CHANGE_PROFILE, 0, dfaflags))
527
if (entry->mode & (AA_USER_PTRACE | AA_OTHER_PTRACE)) {
528
int mode = entry->mode & (AA_USER_PTRACE | AA_OTHER_PTRACE);
529
if (entry->namespace) {
531
vec[0] = entry->namespace;
532
vec[1] = entry->name;
533
if (!aare_add_rule_vec(dfarules, 0, mode, 0, 2, vec, dfaflags))
536
if (!aare_add_rule(dfarules, entry->name, 0, mode, 0, dfaflags))
543
int post_process_entries(struct codomain *cod)
546
struct cod_entry *entry;
549
list_for_each(cod->entries, entry) {
550
if (regex_type == AARE_DFA &&
551
!process_dfa_entry(cod->dfarules, entry))
556
cod->dfarule_count = count;
560
int process_regex(struct codomain *cod)
564
if (regex_type == AARE_DFA) {
565
if (!process_profile_name_xmatch(cod))
568
cod->dfarules = aare_new_ruleset(0);
572
if (!post_process_entries(cod))
575
if (regex_type == AARE_DFA && cod->dfarule_count > 0) {
576
cod->dfa = aare_create_dfa(cod->dfarules, &cod->dfa_size,
578
aare_delete_ruleset(cod->dfarules);
579
cod->dfarules = NULL;
583
if (cod->dfa_size == 0) {
584
PERROR(_("profile %s: has merged rules (%s) with "
585
"multiple x modifiers\n"),
586
cod->name, (char *) cod->dfa);
594
* Post process subdomain(s):
596
* They are chained from the toplevel subdomain pointer
597
* thru each <codomain> next pointer.
599
* i.e first subdomain is list->subdomain
600
* second subdomain is list->subdomain->next
602
* N.B sub-subdomains are not valid so:
603
* if (list->subdomain) {
604
* assert(list->subdomain->subdomain == NULL)
607
if (process_hat_regex(cod) != 0)
616
static int build_list_val_expr(char *buffer, int size, struct value_list *list)
618
struct value_list *ent;
619
char tmp[PATH_MAX + 3];
626
strncpy(buffer, "[^\\000]*", size);
631
strncpy(p, "(", size - (p - buffer));
633
if (p > buffer + size)
636
ptype = convert_aaregex_to_pcre(list->value, 0, tmp, PATH_MAX+3, &pos);
637
if (ptype == ePatternInvalid)
641
if (len > size - (p - buffer))
646
list_for_each(list->next, ent) {
647
ptype = convert_aaregex_to_pcre(ent->value, 0, tmp,
649
if (ptype == ePatternInvalid)
652
strncpy(p, "|", size - (p - buffer));
655
if (len > size - (p - buffer))
660
strncpy(p, ")", size - (p - buffer));
662
if (p > buffer + size)
670
static int convert_entry(char *buffer, int size, char *entry)
676
ptype = convert_aaregex_to_pcre(entry, 0, buffer, size, &pos);
677
if (ptype == ePatternInvalid)
680
/* match any char except \000 0 or more times */
684
strcpy(buffer, "[^\\000]*");
689
static int build_mnt_flags(char *buffer, int size, unsigned int flags,
690
unsigned int inv_flags)
695
if (flags == MS_ALL_FLAGS) {
696
/* all flags are optional */
697
len = snprintf(p, size, "[^\\000]*");
698
if (len < 0 || len >= size)
702
for (i = 0; i <= 31; ++i) {
703
if ((flags & inv_flags) & (1 << i))
704
len = snprintf(p, size, "(\\x%02x|)", i + 1);
705
else if (flags & (1 << i))
706
len = snprintf(p, size, "\\x%02x", i + 1);
707
else /* no entry = not set */
710
if (len < 0 || len >= size)
716
/* this needs to go once the backend is updated. */
718
/* match nothing - use impossible 254 as regex parser doesn't
719
* like the empty string
724
strcpy(p, "(\\xfe|)");
730
static int build_mnt_opts(char *buffer, int size, struct value_list *opts)
732
struct value_list *ent;
733
char tmp[PATH_MAX + 3];
742
strncpy(buffer, "[^\\000]*", size);
747
list_for_each(opts, ent) {
748
ptype = convert_aaregex_to_pcre(ent->value, 0, tmp,
750
if (ptype == ePatternInvalid)
754
if (len > size - (p - buffer))
758
if (ent->next && size - (p - buffer) > 1) {
770
static int process_mnt_entry(aare_ruleset_t *dfarules, struct mnt_entry *entry)
772
char mntbuf[PATH_MAX + 3];
773
char devbuf[PATH_MAX + 3];
774
char typebuf[PATH_MAX + 3];
775
char flagsbuf[PATH_MAX + 3];
776
char optsbuf[PATH_MAX + 3];
779
unsigned int flags, inv_flags;
781
/* a single mount rule may result in multiple matching rules being
782
* created in the backend to cover all the possible choices
785
if ((entry->allow & AA_MAY_MOUNT) && (entry->flags & MS_REMOUNT)
786
&& !entry->device && !entry->dev_type) {
788
/* remount can't be conditional on device and type */
790
/* rule class single byte header */
791
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
792
if (entry->mnt_point) {
793
/* both device && mnt_point or just mnt_point */
794
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
798
if (!convert_entry(p, PATH_MAX +3, entry->device))
803
if (!convert_entry(devbuf, PATH_MAX +3, NULL))
809
flags = entry->flags;
810
inv_flags = entry->inv_flags;
811
if (flags != MS_ALL_FLAGS)
812
flags &= MS_REMOUNT_FLAGS;
813
if (inv_flags != MS_ALL_FLAGS)
814
flags &= MS_REMOUNT_FLAGS;
815
if (!build_mnt_flags(flagsbuf, PATH_MAX, flags, inv_flags))
818
if (!build_mnt_opts(optsbuf, PATH_MAX, entry->opts))
822
allow = AA_MATCH_CONT;
824
allow = entry->allow;
826
/* rule for match without required data || data MATCH_CONT */
827
if (!aare_add_rule_vec(dfarules, entry->deny, allow,
828
entry->audit | AA_AUDIT_MNT_DATA, 4,
834
/* rule with data match required */
835
if (!build_mnt_opts(optsbuf, PATH_MAX, entry->opts))
838
if (!aare_add_rule_vec(dfarules, entry->deny,
840
entry->audit | AA_AUDIT_MNT_DATA,
846
if ((entry->allow & AA_MAY_MOUNT) && (entry->flags & MS_BIND)
847
&& !entry->dev_type && !entry->opts) {
848
/* bind mount rules can't be conditional on dev_type or data */
850
/* rule class single byte header */
851
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
852
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
855
if (!convert_entry(devbuf, PATH_MAX +3, entry->device))
858
if (!convert_entry(typebuf, PATH_MAX +3, NULL))
862
flags = entry->flags;
863
inv_flags = entry->inv_flags;
864
if (flags != MS_ALL_FLAGS)
865
flags &= MS_BIND_FLAGS;
866
if (inv_flags != MS_ALL_FLAGS)
867
flags &= MS_BIND_FLAGS;
868
if (!build_mnt_flags(flagsbuf, PATH_MAX, flags, inv_flags))
871
if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
872
entry->audit, 4, vec, dfaflags))
876
if ((entry->allow & AA_MAY_MOUNT) &&
877
(entry->flags & (MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED))
878
&& !entry->device && !entry->dev_type && !entry->opts) {
879
/* change type base rules can not be conditional on device,
880
* device type or data
883
/* rule class single byte header */
884
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
885
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
888
/* skip device and type */
889
if (!convert_entry(devbuf, PATH_MAX +3, NULL))
894
flags = entry->flags;
895
inv_flags = entry->inv_flags;
896
if (flags != MS_ALL_FLAGS)
897
flags &= MS_MAKE_FLAGS;
898
if (inv_flags != MS_ALL_FLAGS)
899
flags &= MS_MAKE_FLAGS;
900
if (!build_mnt_flags(flagsbuf, PATH_MAX, flags, inv_flags))
903
if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
904
entry->audit, 4, vec, dfaflags))
908
if ((entry->allow & AA_MAY_MOUNT) && (entry->flags & MS_MOVE)
909
&& !entry->dev_type && !entry->opts) {
910
/* mount move rules can not be conditional on dev_type,
914
/* rule class single byte header */
915
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
916
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
919
if (!convert_entry(devbuf, PATH_MAX +3, entry->device))
923
if (!convert_entry(typebuf, PATH_MAX +3, NULL))
927
flags = entry->flags;
928
inv_flags = entry->inv_flags;
929
if (flags != MS_ALL_FLAGS)
930
flags &= MS_MOVE_FLAGS;
931
if (inv_flags != MS_ALL_FLAGS)
932
flags &= MS_MOVE_FLAGS;
933
if (!build_mnt_flags(flagsbuf, PATH_MAX, flags, inv_flags))
936
if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
937
entry->audit, 4, vec, dfaflags))
941
if ((entry->allow & AA_MAY_MOUNT) &&
942
(entry->flags | entry->inv_flags) & ~MS_CMDS) {
944
/* generic mount if flags are set that are not covered by
948
/* rule class single byte header */
949
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
950
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
953
if (!convert_entry(devbuf, PATH_MAX +3, entry->device))
956
if (!build_list_val_expr(typebuf, PATH_MAX+2, entry->dev_type))
960
flags = entry->flags;
961
inv_flags = entry->inv_flags;
962
if (flags != MS_ALL_FLAGS)
964
if (inv_flags != MS_ALL_FLAGS)
966
if (!build_mnt_flags(flagsbuf, PATH_MAX, flags, inv_flags))
971
allow = AA_MATCH_CONT;
973
allow = entry->allow;
975
/* rule for match without required data || data MATCH_CONT */
976
if (!aare_add_rule_vec(dfarules, entry->deny, allow,
977
entry->audit | AA_AUDIT_MNT_DATA, 4,
983
/* rule with data match required */
984
if (!build_mnt_opts(optsbuf, PATH_MAX, entry->opts))
987
if (!aare_add_rule_vec(dfarules, entry->deny,
989
entry->audit | AA_AUDIT_MNT_DATA,
995
if (entry->allow & AA_MAY_UMOUNT) {
997
/* rule class single byte header */
998
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
999
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
1002
if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
1003
entry->audit, 1, vec, dfaflags))
1007
if (entry->allow & AA_MAY_PIVOTROOT) {
1009
/* rule class single byte header */
1010
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
1011
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
1014
if (!convert_entry(devbuf, PATH_MAX +3, entry->device))
1017
if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
1018
entry->audit, 2, vec, dfaflags))
1024
/* didn't actually encode anything */
1030
PERROR("Enocoding of mount rule failed\n");
1035
int post_process_policydb_ents(struct codomain *cod)
1040
/* Add fns for rules that should be added to policydb here */
1041
if (cod->mnt_ents && kernel_supports_mount) {
1042
struct mnt_entry *entry;
1043
list_for_each(cod->mnt_ents, entry) {
1044
if (regex_type == AARE_DFA &&
1045
!process_mnt_entry(cod->policy_rules, entry))
1049
} else if (cod->mnt_ents && !kernel_supports_mount)
1050
pwarn("profile %s mount rules not enforced\n", cod->name);
1052
cod->policy_rule_count = count;
1056
int process_policydb(struct codomain *cod)
1060
if (regex_type == AARE_DFA) {
1061
cod->policy_rules = aare_new_ruleset(0);
1062
if (!cod->policy_rules)
1066
if (!post_process_policydb_ents(cod))
1069
if (regex_type == AARE_DFA && cod->policy_rule_count > 0) {
1070
cod->policy_dfa = aare_create_dfa(cod->policy_rules,
1071
&cod->policy_dfa_size,
1073
aare_delete_ruleset(cod->policy_rules);
1074
cod->policy_rules = NULL;
1075
if (!cod->policy_dfa)
1079
aare_reset_matchflags();
1080
if (process_hat_policydb(cod) != 0)
1089
void reset_regex(void)
1091
aare_reset_matchflags();
1095
static int test_filter_slashes(void)
1100
test_string = strdup("///foo//////f//oo////////////////");
1101
filter_slashes(test_string);
1102
MY_TEST(strcmp(test_string, "/foo/f/oo/") == 0, "simple tests");
1104
test_string = strdup("/foo/f/oo");
1105
filter_slashes(test_string);
1106
MY_TEST(strcmp(test_string, "/foo/f/oo") == 0, "simple test for no changes");
1108
test_string = strdup("/");
1109
filter_slashes(test_string);
1110
MY_TEST(strcmp(test_string, "/") == 0, "simple test for '/'");
1112
test_string = strdup("");
1113
filter_slashes(test_string);
1114
MY_TEST(strcmp(test_string, "") == 0, "simple test for ''");
1116
test_string = strdup("//usr");
1117
filter_slashes(test_string);
1118
MY_TEST(strcmp(test_string, "//usr") == 0, "simple test for // namespace");
1120
test_string = strdup("//");
1121
filter_slashes(test_string);
1122
MY_TEST(strcmp(test_string, "//") == 0, "simple test 2 for // namespace");
1124
test_string = strdup("///usr");
1125
filter_slashes(test_string);
1126
MY_TEST(strcmp(test_string, "/usr") == 0, "simple test for ///usr");
1128
test_string = strdup("///");
1129
filter_slashes(test_string);
1130
MY_TEST(strcmp(test_string, "/") == 0, "simple test for ///");
1132
test_string = strdup("/a/");
1133
filter_slashes(test_string);
1134
MY_TEST(strcmp(test_string, "/a/") == 0, "simple test for /a/");
1144
retval = test_filter_slashes();
1150
#endif /* UNIT_TEST */