diff --git a/native/jni/Android.mk b/native/jni/Android.mk index cb5a63cca..9b9ac07ce 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -73,10 +73,10 @@ LOCAL_C_INCLUDES := \ $(LIBUTILS) LOCAL_SRC_FILES := \ - misc/init.c \ - magiskpolicy/api.c \ - magiskpolicy/magiskpolicy.c \ - magiskpolicy/rules.c \ + misc/init.cpp \ + magiskpolicy/api.cpp \ + magiskpolicy/magiskpolicy.cpp \ + magiskpolicy/rules.cpp \ magiskpolicy/sepolicy.c LOCAL_LDFLAGS := -static diff --git a/native/jni/include/magiskrc.h b/native/jni/include/magiskrc.h index fca8af782..43d2dd3ad 100644 --- a/native/jni/include/magiskrc.h +++ b/native/jni/include/magiskrc.h @@ -6,22 +6,22 @@ static const char magiskrc[] = "on post-fs-data\n" " start logd\n" " load_persist_props\n" -" rm "UNBLOCKFILE"\n" +" rm " UNBLOCKFILE "\n" " start %s\n" -" wait "UNBLOCKFILE" 10\n" -" rm "UNBLOCKFILE"\n" +" wait " UNBLOCKFILE " 10\n" +" rm " UNBLOCKFILE "\n" "\n" "service %s /sbin/magisk --startup\n" " user root\n" -" seclabel u:r:"SEPOL_PROC_DOMAIN":s0\n" +" seclabel u:r:" SEPOL_PROC_DOMAIN ":s0\n" " oneshot\n" "\n" "service %s /sbin/magisk --service\n" " class late_start\n" " user root\n" -" seclabel u:r:"SEPOL_PROC_DOMAIN":s0\n" +" seclabel u:r:" SEPOL_PROC_DOMAIN ":s0\n" " oneshot\n" "\n" diff --git a/native/jni/magiskpolicy/api.c b/native/jni/magiskpolicy/api.cpp similarity index 55% rename from native/jni/magiskpolicy/api.c rename to native/jni/magiskpolicy/api.cpp index 2193bc510..6ee0fefda 100644 --- a/native/jni/magiskpolicy/api.c +++ b/native/jni/magiskpolicy/api.cpp @@ -1,28 +1,28 @@ #include "magiskpolicy.h" #include "sepolicy.h" -int sepol_allow(char *s, char *t, char *c, char *p) { - // printf("allow %s %s %s %s\n", s, t, c, p); +int sepol_allow(const char *s, const char *t, const char *c, const char *p) { +// printf("allow %s %s %s %s\n", s, t, c, p); return add_rule(s, t, c, p, AVTAB_ALLOWED, 0); } -int sepol_deny(char *s, char *t, char *c, char *p) { +int sepol_deny(const char *s, const char *t, const char *c, const char *p) { // printf("deny %s %s %s %s\n", s, t, c, p); return add_rule(s, t, c, p, AVTAB_ALLOWED, 1); } -int sepol_auditallow(char *s, char *t, char *c, char *p) { +int sepol_auditallow(const char *s, const char *t, const char *c, const char *p) { // printf("auditallow %s %s %s %s\n", s, t, c, p); return add_rule(s, t, c, p, AVTAB_AUDITALLOW, 0); } -int sepol_auditdeny(char *s, char *t, char *c, char *p) { +int sepol_auditdeny(const char *s, const char *t, const char *c, const char *p) { // printf("auditdeny %s %s %s %s\n", s, t, c, p); return add_rule(s, t, c, p, AVTAB_AUDITDENY, 0); } -int sepol_typetrans(char *s, char *t, char *c, char *d, char *o) { - if (o == NULL) { +int sepol_typetrans(const char *s, const char *t, const char *c, const char *d, const char *o) { + if (o == nullptr) { // printf("add_trans %s %s %s %s\n", s, t, c ,d); return add_transition(s, t, c, d); } else { @@ -31,41 +31,41 @@ int sepol_typetrans(char *s, char *t, char *c, char *d, char *o) { } } -int sepol_allowxperm(char *s, char *t, char *c, char *range) { +int sepol_allowxperm(const char *s, const char *t, const char *c, const char *range) { // printf("allowxperm %s %s %s %s\n", s, t, c, range); return add_xperm_rule(s, t, c, range, AVTAB_XPERMS_ALLOWED, 0); } -int sepol_auditallowxperm(char *s, char *t, char *c, char *range) { +int sepol_auditallowxperm(const char *s, const char *t, const char *c, const char *range) { // printf("auditallowxperm %s %s %s %s\n", s, t, c, range); return add_xperm_rule(s, t, c, range, AVTAB_XPERMS_AUDITALLOW, 0); } -int sepol_dontauditxperm(char *s, char *t, char *c, char *range) { +int sepol_dontauditxperm(const char *s, const char *t, const char *c, const char *range) { // printf("dontauditxperm %s %s %s %s\n", s, t, c, range); return add_xperm_rule(s, t, c, range, AVTAB_XPERMS_DONTAUDIT, 0); } -int sepol_permissive(char *s) { +int sepol_permissive(const char *s) { // printf("permissive %s\n", s); return set_domain_state(s, 1); } -int sepol_enforce(char *s) { +int sepol_enforce(const char *s) { // printf("enforce %s\n", s); return set_domain_state(s, 0); } -int sepol_create(char *s) { +int sepol_create(const char *s) { // printf("create %s\n", s); return create_domain(s); } -int sepol_attradd(char *s, char *a) { +int sepol_attradd(const char *s, const char *a) { // printf("attradd %s %s\n", s, a); return add_typeattribute(s, a); } -int sepol_exists(char* source) { - return !! hashtab_search(policydb->p_types.table, source); +int sepol_exists(const char *source) { + return hashtab_search(policydb->p_types.table, source) != nullptr; } diff --git a/native/jni/magiskpolicy/magiskpolicy.c b/native/jni/magiskpolicy/magiskpolicy.c deleted file mode 100644 index 201339d8a..000000000 --- a/native/jni/magiskpolicy/magiskpolicy.c +++ /dev/null @@ -1,500 +0,0 @@ -/* magiskpolicy.c - Main function for policy patching - * - * Includes all the parsing logic for the policy statements - */ - -#include -#include - -#include "sepolicy.h" -#include "vector.h" -#include "magiskpolicy.h" -#include "magisk.h" -#include "flags.h" - -static int syntax_err = 0; -static char err_msg[ARG_MAX]; - -static void statements() { - fprintf(stderr, - "One policy statement should be treated as one parameter;\n" - "this means a full policy statement should be enclosed in quotes;\n" - "multiple policy statements can be provided in a single command\n" - "\n" - "The statements has a format of \" [args...]\"\n" - "Use '*' in args to represent every possible match.\n" - "Collections wrapped in curly brackets can also be used as args.\n" - "\n" - "Supported policy statements:\n" - "\n" - "Type 1:\n" - "\" source-class target-class permission-class permission\"\n" - "Action: allow, deny, auditallow, auditdeny\n" - "\n" - "Type 2:\n" - "\" source-class target-class permission-class ioctl range\"\n" - "Action: allowxperm, auditallowxperm, dontauditxperm\n" - "\n" - "Type 3:\n" - "\" class\"\n" - "Action: create, permissive, enforcing\n" - "\n" - "Type 4:\n" - "\"attradd class attribute\"\n" - "\n" - "Type 5:\n" - "\"typetrans source-class target-class permission-class default-class (optional: object-name)\"\n" - "\n" - "Notes:\n" - "- typetrans does not support the all match '*' syntax\n" - "- permission-class cannot be collections\n" - "- source-class and target-class can also be attributes\n" - "\n" - "Example: allow { source1 source2 } { target1 target2 } permission-class *\n" - "Will be expanded to:\n" - "\n" - "allow source1 target1 permission-class { all-permissions }\n" - "allow source1 target2 permission-class { all-permissions }\n" - "allow source2 target1 permission-class { all-permissions }\n" - "allow source2 target2 permission-class { all-permissions }\n" - "\n" - ); -} - -static void usage(char *arg0) { - fprintf(stderr, - "MagiskPolicy v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu)\n\n" - "Usage: %s [--options...] [policy statements...]\n" - "\n" - "Options:\n" - " --live directly apply sepolicy live\n" - " --magisk inject built-in rules for a minimal\n" - " Magisk selinux environment\n" - " --load FILE load policies from FILE\n" - " --compile-split compile and load split cil policies\n" - " from system and vendor just like init\n" - " --save FILE save policies to FILE\n" - "\n" - "If neither --load or --compile-split is specified, it will load\n" - "from current live policies (" SELINUX_POLICY ")\n" - "\n" - , arg0); - statements(); - exit(1); -} - -// Pattern 1: action { source } { target } class { permission } -static int parse_pattern_1(int action, char* statement) { - int state = 0, in_bracket = 0; - char *tok, *class, *saveptr; - struct vector source, target, permission; - vec_init(&source); - vec_init(&target); - vec_init(&permission); - tok = strtok_r(statement, " ", &saveptr); - while (tok != NULL) { - if (tok[0] == '{') { - if (in_bracket || state == 2) return 1; - in_bracket = 1; - if (tok[1]) { - ++tok; - continue; - } - } else if (tok[strlen(tok) - 1] == '}') { - if (!in_bracket || state == 2) return 1; - in_bracket = 0; - if (strlen(tok) - 1) { - tok[strlen(tok) - 1] = '\0'; - continue; - } - } else { - if (tok[0] == '*') tok = ALL; - struct vector *vec; - switch (state) { - case 0: - vec = &source; - break; - case 1: - vec = ⌖ - break; - case 2: - vec = NULL; - class = tok; - break; - case 3: - vec = &permission; - break; - default: - return 1; - } - vec_push_back(vec, tok); - } - if (!in_bracket) ++state; - tok = strtok_r(NULL, " ", &saveptr); - } - if (state != 4) return 1; - for(int i = 0; i < source.size; ++i) - for (int j = 0; j < target.size; ++j) - for (int k = 0; k < permission.size; ++k) { - int (*action_func)(char*, char*, char*, char*); - char *action_str; - switch (action) { - case 0: - action_func = sepol_allow; - action_str = "allow"; - break; - case 1: - action_func = sepol_deny; - action_str = "deny"; - break; - case 2: - action_func = sepol_auditallow; - action_str = "auditallow"; - break; - case 3: - action_func = sepol_auditdeny; - action_str = "auditdeny"; - break; - default: - return 1; - } - if (action_func(source.data[i], target.data[j], class, permission.data[k])) - fprintf(stderr, "Error in: %s %s %s %s %s\n", - action_str, (char *) source.data[i], (char *) target.data[j], class, (char *) permission.data[k]); - } - vec_destroy(&source); - vec_destroy(&target); - vec_destroy(&permission); - return 0; -} - -// Pattern 2: action { class } { attribute } -static int parse_pattern_2(int action, char* statement) { - int state = 0, in_bracket = 0; - char *tok, *saveptr; - struct vector class, attribute; - vec_init(&class); - vec_init(&attribute); - tok = strtok_r(statement, " ", &saveptr); - while (tok != NULL) { - if (tok[0] == '{') { - if (in_bracket) return 1; - in_bracket = 1; - if (tok[1]) { - ++tok; - continue; - } - } else if (tok[strlen(tok) - 1] == '}') { - if (!in_bracket) return 1; - in_bracket = 0; - if (strlen(tok) - 1) { - tok[strlen(tok) - 1] = '\0'; - continue; - } - } else { - if (tok[0] == '*') tok = ALL; - struct vector *vec; - switch (state) { - case 0: - vec = &class; - break; - case 1: - vec = &attribute; - break; - default: - return 1; - } - vec_push_back(vec, tok); - } - if (!in_bracket) ++state; - tok = strtok_r(NULL, " ", &saveptr); - } - if (state != 2) return 1; - for(int i = 0; i < class.size; ++i) - for (int j = 0; j < attribute.size; ++j) { - int (*action_func)(char*, char*); - char *action_str; - switch (action) { - case 0: - action_func = sepol_attradd; - action_str = "attradd"; - break; - default: - return 1; - } - if (action_func(class.data[i], attribute.data[j])) - fprintf(stderr, "Error in: %s %s %s\n", - action_str, (char *) class.data[i], (char *) attribute.data[j]); - } - vec_destroy(&class); - vec_destroy(&attribute); - return 0; -} - -// Pattern 3: action { type } -static int parse_pattern_3(int action, char* statement) { - char *tok, *saveptr; - struct vector classes; - vec_init(&classes); - tok = strtok_r(statement, " {}", &saveptr); - while (tok != NULL) { - if (tok[0] == '*') tok = ALL; - vec_push_back(&classes, tok); - tok = strtok_r(NULL, " {}", &saveptr); - } - for (int i = 0; i < classes.size; ++i) { - int (*action_func)(char*); - char *action_str; - switch (action) { - case 0: - action_func = sepol_create; - action_str = "create"; - break; - case 1: - action_func = sepol_permissive; - action_str = "permissive"; - break; - case 2: - action_func = sepol_enforce; - action_str = "enforce"; - break; - } - if (action_func(classes.data[i])) - fprintf(stderr, "Error in: %s %s\n", action_str, (char *) classes.data[i]); - } - vec_destroy(&classes); - return 0; -} - -// Pattern 4: action source target class default (filename) -static int parse_pattern_4(int action, char* statement) { - int state = 0; - char *tok, *saveptr; - char *source, *target, *class, *def, *filename = NULL; - tok = strtok_r(statement, " ", &saveptr); - while (tok != NULL) { - switch(state) { - case 0: - source = tok; - break; - case 1: - target = tok; - break; - case 2: - class = tok; - break; - case 3: - def = tok; - break; - case 4: - filename = tok; - break; - default: - return 1; - } - tok = strtok_r(NULL, " ", &saveptr); - ++state; - } - if (state < 4) return 1; - if (sepol_typetrans(source, target, class, def, filename)) - fprintf(stderr, "Error in: typetrans %s %s %s %s %s\n", source, target, class, def, filename ? filename : ""); - return 0; -} - -// Pattern 5: action { source } { target } { class } ioctl range -static int parse_pattern_5(int action, char* statement) { - int state = 0, in_bracket = 0; - char *tok, *range, *saveptr; - struct vector source, target, class; - vec_init(&source); - vec_init(&target); - vec_init(&class); - tok = strtok_r(statement, " ", &saveptr); - while (tok != NULL) { - if (tok[0] == '{') { - if (in_bracket || state == 3 || state == 4) return 1; - in_bracket = 1; - if (tok[1]) { - ++tok; - continue; - } - } else if (tok[strlen(tok) - 1] == '}') { - if (!in_bracket || state == 3 || state == 4) return 1; - in_bracket = 0; - if (strlen(tok) - 1) { - tok[strlen(tok) - 1] = '\0'; - continue; - } - } else { - if (tok[0] == '*') tok = ALL; - struct vector *vec; - switch (state) { - case 0: - vec = &source; - break; - case 1: - vec = ⌖ - break; - case 2: - vec = &class; - break; - case 3: - // Should always be ioctl - vec = NULL; - break; - case 4: - vec = NULL; - range = tok; - break; - default: - return 1; - } - vec_push_back(vec, tok); - } - if (!in_bracket) ++state; - tok = strtok_r(NULL, " ", &saveptr); - } - if (state != 5) return 1; - for(int i = 0; i < source.size; ++i) - for (int j = 0; j < target.size; ++j) - for (int k = 0; k < class.size; ++k) { - int (*action_func)(char*, char*, char*, char*); - char *action_str; - switch (action) { - case 0: - action_func = sepol_allowxperm; - action_str = "allowxperm"; - break; - case 1: - action_func = sepol_auditallowxperm; - action_str = "auditallowxperm"; - break; - case 2: - action_func = sepol_dontauditxperm; - action_str = "dontauditxperm"; - break; - default: - return 1; - } - if (action_func(source.data[i], target.data[j], class.data[k], range)) - fprintf(stderr, "Error in: %s %s %s %s %s\n", - action_str, (char *) source.data[i], (char *) target.data[j], (char *) class.data[k], range); - } - vec_destroy(&source); - vec_destroy(&target); - vec_destroy(&class); - return 0; -} - -static void syntax_error_msg() { - fprintf(stderr, "Syntax error in \"%s\"\n", err_msg); - syntax_err = 1; -} - -int magiskpolicy_main(int argc, char *argv[]) { - char *outfile = NULL, *tok, *saveptr; - int magisk = 0; - struct vector rules; - - vec_init(&rules); - - if (argc < 2) usage(argv[0]); - for (int i = 1; i < argc; ++i) { - if (argv[i][0] == '-' && argv[i][1] == '-') { - if (strcmp(argv[i] + 2, "live") == 0) - outfile = SELINUX_LOAD; - else if (strcmp(argv[i] + 2, "magisk") == 0) - magisk = 1; - else if (strcmp(argv[i] + 2, "load") == 0) { - if (i + 1 >= argc) - usage(argv[0]); - if (load_policydb(argv[i + 1])) { - fprintf(stderr, "Cannot load policy from %s\n", argv[i + 1]); - return 1; - } - ++i; - } else if (strcmp(argv[i] + 2, "compile-split") == 0) { - if (compile_split_cil()) { - fprintf(stderr, "Cannot compile split cil\n"); - return 1; - } - } else if (strcmp(argv[i] + 2, "save") == 0) { - if (i + 1 >= argc) - usage(argv[0]); - outfile = argv[i + 1]; - ++i; - } else { - usage(argv[0]); - } - } else { - vec_push_back(&rules, argv[i]); - } - } - - // Use current policy if nothing is loaded - if(policydb == NULL && load_policydb(SELINUX_POLICY)) { - fprintf(stderr, "Cannot load policy from " SELINUX_POLICY "\n"); - return 1; - } - - if (magisk) - sepol_magisk_rules(); - - for (int i = 0; i < rules.size; ++i) { - // Since strtok will modify the origin string, copy the policy for error messages - strcpy(err_msg, rules.data[i]); - tok = strtok_r(rules.data[i], " ", &saveptr); - if (strcmp(tok, "allow") == 0) { - if (parse_pattern_1(0, rules.data[i] + strlen(tok) + 1)) - syntax_error_msg(); - } else if (strcmp(tok, "deny") == 0) { - if (parse_pattern_1(1, rules.data[i] + strlen(tok) + 1)) - syntax_error_msg(); - } else if (strcmp(tok, "auditallow") == 0) { - if (parse_pattern_1(2, rules.data[i] + strlen(tok) + 1)) - syntax_error_msg(); - } else if (strcmp(tok, "auditdeny") == 0) { - if (parse_pattern_1(3, rules.data[i] + strlen(tok) + 1)) - syntax_error_msg(); - } else if (strcmp(tok, "attradd") == 0) { - if (parse_pattern_2(0, rules.data[i] + strlen(tok) + 1)) - syntax_error_msg(); - } else if (strcmp(tok, "create") == 0) { - if (parse_pattern_3(0, rules.data[i] + strlen(tok) + 1)) - syntax_error_msg(); - } else if (strcmp(tok, "permissive") == 0) { - if (parse_pattern_3(1, rules.data[i] + strlen(tok) + 1)) - syntax_error_msg(); - } else if (strcmp(tok, "enforce") == 0) { - if (parse_pattern_3(2, rules.data[i] + strlen(tok) + 1)) - syntax_error_msg(); - } else if (strcmp(tok, "typetrans") == 0) { - if (parse_pattern_4(0, rules.data[i] + strlen(tok) + 1)) - syntax_error_msg(); - } else if (strcmp(tok, "allowxperm") == 0) { - if (parse_pattern_5(0, rules.data[i] + strlen(tok) + 1)) - syntax_error_msg(); - } else if (strcmp(tok, "auditallowxperm") == 0) { - if (parse_pattern_5(1, rules.data[i] + strlen(tok) + 1)) - syntax_error_msg(); - } else if (strcmp(tok, "dontauditxperm") == 0) { - if (parse_pattern_5(2, rules.data[i] + strlen(tok) + 1)) - syntax_error_msg(); - } else { - syntax_error_msg(); - } - } - - if (syntax_err) - statements(); - - vec_destroy(&rules); - - if (outfile && dump_policydb(outfile)) { - fprintf(stderr, "Cannot dump policy to %s\n", outfile); - return 1; - } - - destroy_policydb(); - return 0; -} diff --git a/native/jni/magiskpolicy/magiskpolicy.cpp b/native/jni/magiskpolicy/magiskpolicy.cpp new file mode 100644 index 000000000..184388092 --- /dev/null +++ b/native/jni/magiskpolicy/magiskpolicy.cpp @@ -0,0 +1,462 @@ +/* magiskpolicy.cpp - Main function for policy patching + * + * Includes all the parsing logic for the policy statements + */ + +#include +#include + +#include "sepolicy.h" +#include "array.h" +#include "magiskpolicy.h" +#include "magisk.h" +#include "flags.h" + +static const char *type_msg_1 = +"Type 1:\n" +"\" source-class target-class permission-class permission\"\n" +"Action: allow, deny, auditallow, auditdeny\n"; + +static const char *type_msg_2 = +"Type 2:\n" +"\" source-class target-class permission-class ioctl range\"\n" +"Action: allowxperm, auditallowxperm, dontauditxperm\n"; + +static const char *type_msg_3 = +"Type 3:\n" +"\" class\"\n" +"Action: create, permissive, enforcing\n"; + +static const char *type_msg_4 = +"Type 4:\n" +"\"attradd class attribute\"\n"; + +static const char *type_msg_5 = +"Type 5:\n" +"\"typetrans source-class target-class permission-class default-class (optional: object-name)\"\n"; + + +[[noreturn]] static void statements() { + fprintf(stderr, + "One policy statement should be treated as one parameter;\n" + "this means a full policy statement should be enclosed in quotes;\n" + "multiple policy statements can be provided in a single command\n" + "\n" + "The statements has a format of \" [args...]\"\n" + "Use '*' in args to represent every possible match.\n" + "Collections wrapped in curly brackets can also be used as args.\n" + "\n" + "Supported policy statements:\n" + "\n" + "%s\n" + "%s\n" + "%s\n" + "%s\n" + "%s\n" + "Notes:\n" + "- typetrans does not support the all match '*' syntax\n" + "- permission-class cannot be collections\n" + "- source-class and target-class can also be attributes\n" + "\n" + "Example: allow { source1 source2 } { target1 target2 } permission-class *\n" + "Will be expanded to:\n" + "\n" + "allow source1 target1 permission-class { all-permissions }\n" + "allow source1 target2 permission-class { all-permissions }\n" + "allow source2 target1 permission-class { all-permissions }\n" + "allow source2 target2 permission-class { all-permissions }\n" + "\n", + type_msg_1, type_msg_2, type_msg_3, type_msg_4, type_msg_5); + exit(0); +} + +[[noreturn]] static void usage(char *arg0) { + fprintf(stderr, + "MagiskPolicy v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu)\n\n" + "Usage: %s [--options...] [policy statements...]\n" + "\n" + "Options:\n" + " --help show help message for policy statements\n" + " --load FILE load policies from FILE\n" + " --compile-split compile and load split cil policies\n" + " from system and vendor just like init\n" + " --save FILE save policies to FILE\n" + " --live directly apply sepolicy live\n" + " --magisk inject built-in rules for a minimal\n" + " Magisk selinux environment\n" + "\n" + "If neither --load or --compile-split is specified, it will load\n" + "from current live policies (" SELINUX_POLICY ")\n" + "\n", + arg0); + exit(1); +} + +static char *parse_bracket(char *str, Array *vec) { + str = strchr(str, '{') + 1; + char *end = strchr(str, '}'); + if (end == nullptr) + return nullptr; + *end = '\0'; + char *cur; + while ((cur = strtok_r(nullptr, " ", &str)) != nullptr) + vec->push_back(cur); + return end + 1; +} + +// Pattern 1: action { source } { target } class { permission } +static int parse_pattern_1(int action, char *stmt) { + int state = 0; + char *cur, *cls; + Array source, target, permission; + while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) { + if (cur[0] == '*') cur = ALL; + Array *vec; + switch (state) { + case 0: + vec = &source; + break; + case 1: + vec = ⌖ + break; + case 2: + vec = nullptr; + cls = cur; + break; + case 3: + vec = &permission; + break; + default: + return 1; + } + + if (vec) { + if (cur == nullptr || cur[0] != '{') { + vec->push_back(cur); + } else { + stmt[-1] = ' '; + stmt = parse_bracket(cur, vec); + if (stmt == nullptr) + return 1; + } + } + ++state; + } + if (state != 4) + return 1; + for(int i = 0; i < source.size(); ++i) + for (int j = 0; j < target.size(); ++j) + for (int k = 0; k < permission.size(); ++k) { + int (*action_func)(const char*, const char*, const char*, const char*); + const char *action_str; + switch (action) { + case 0: + action_func = sepol_allow; + action_str = "allow"; + break; + case 1: + action_func = sepol_deny; + action_str = "deny"; + break; + case 2: + action_func = sepol_auditallow; + action_str = "auditallow"; + break; + case 3: + action_func = sepol_auditdeny; + action_str = "auditdeny"; + break; + default: + return 1; + } + if (action_func(source[i], target[j], cls, permission[k])) + fprintf(stderr, "Error in: %s %s %s %s %s\n", + action_str, source[i], target[j], cls, permission[k]); + } + return 0; +} + +// Pattern 2: action { source } { target } { class } ioctl range +static int parse_pattern_2(int action, char *stmt) { + int state = 0; + char *cur, *range; + Array source, target, cls; + while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) { + if (cur[0] == '*') cur = ALL; + Array *vec; + switch (state) { + case 0: + vec = &source; + break; + case 1: + vec = ⌖ + break; + case 2: + vec = &cls; + break; + case 3: + // Currently only support ioctl + vec = nullptr; + break; + case 4: + vec = nullptr; + range = cur; + break; + default: + return 1; + } + + if (vec) { + if (cur == nullptr || cur[0] != '{') { + vec->push_back(cur); + } else { + stmt[-1] = ' '; + stmt = parse_bracket(cur, vec); + if (stmt == nullptr) + return 1; + } + } + ++state; + } + if (state != 5) return 1; + for(int i = 0; i < source.size(); ++i) + for (int j = 0; j < target.size(); ++j) + for (int k = 0; k < cls.size(); ++k) { + int (*action_func)(const char*, const char*, const char*, const char*); + const char *action_str; + switch (action) { + case 0: + action_func = sepol_allowxperm; + action_str = "allowxperm"; + break; + case 1: + action_func = sepol_auditallowxperm; + action_str = "auditallowxperm"; + break; + case 2: + action_func = sepol_dontauditxperm; + action_str = "dontauditxperm"; + break; + default: + return 1; + } + if (action_func(source[i], target[j], cls[k], range)) + fprintf(stderr, "Error in: %s %s %s %s %s\n", + action_str, source[i], target[j], cls[k], range); + } + return 0; +} + +// Pattern 3: action { type } +static int parse_pattern_3(int action, char* stmt) { + char *cur; + Array domains; + while ((cur = strtok_r(nullptr, " {}", &stmt)) != nullptr) { + if (cur[0] == '*') cur = ALL; + domains.push_back(cur); + } + for (int i = 0; i < domains.size(); ++i) { + int (*action_func)(const char*); + const char *action_str; + switch (action) { + case 0: + action_func = sepol_create; + action_str = "create"; + break; + case 1: + action_func = sepol_permissive; + action_str = "permissive"; + break; + case 2: + action_func = sepol_enforce; + action_str = "enforce"; + break; + default: + return 1; + } + if (action_func(domains[i])) + fprintf(stderr, "Error in: %s %s\n", action_str, domains[i]); + } + return 0; +} + +// Pattern 4: action { class } { attribute } +static int parse_pattern_4(int action, char *stmt) { + int state = 0; + char *cur; + Array cls, attribute; + while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) { + if (cur[0] == '*') cur = ALL; + Array *vec; + switch (state) { + case 0: + vec = &cls; + break; + case 1: + vec = &attribute; + break; + default: + return 1; + } + + if (cur == nullptr || cur[0] != '{') { + vec->push_back(cur); + } else { + stmt[-1] = ' '; + stmt = parse_bracket(cur, vec); + if (stmt == nullptr) + return 1; + } + ++state; + } + if (state != 2) return 1; + for(int i = 0; i < cls.size(); ++i) + for (int j = 0; j < attribute.size(); ++j) { + int (*action_func)(const char*, const char*); + const char *action_str; + switch (action) { + case 0: + action_func = sepol_attradd; + action_str = "attradd"; + break; + default: + return 1; + } + if (action_func(cls[i], attribute[j])) + fprintf(stderr, "Error in: %s %s %s\n", action_str, cls[i], attribute[j]); + } + return 0; +} + +// Pattern 5: action source target class default (filename) +static int parse_pattern_5(int action, char *stmt) { + int state = 0; + char *cur; + char *source, *target, *cls, *def, *filename = nullptr; + while ((cur = strtok_r(nullptr, " ", &stmt)) != nullptr) { + switch(state) { + case 0: + source = cur; + break; + case 1: + target = cur; + break; + case 2: + cls = cur; + break; + case 3: + def = cur; + break; + case 4: + filename = cur; + break; + default: + return 1; + } + ++state; + } + if (state < 4) return 1; + if (sepol_typetrans(source, target, cls, def, filename)) + fprintf(stderr, "Error in: typetrans %s %s %s %s %s\n", source, target, cls, def, filename ? filename : ""); + return 0; +} + +#define add_action(name, type, num) \ +else if (strcmp(name, action) == 0) { \ + if (parse_pattern_##type(num, remain)) \ + fprintf(stderr, "Syntax error in '%s'\n\n%s\n", orig, type_msg_##type); \ +} + +static void parse_statement(char *statement) { + char *action, *remain; + + // strtok will modify the origin string, duplicate the statement for error messages + char *orig = strdup(statement); + + action = strtok_r(statement, " ", &remain); + if (remain == nullptr) remain = &action[strlen(action)]; + + if (0) {} + add_action("allow", 1, 0) + add_action("deny", 1, 1) + add_action("auditallow", 1, 2) + add_action("auditdeny", 1, 3) + add_action("allowxperm", 2, 0) + add_action("auditallowxperm", 2, 1) + add_action("dontauditxperm", 2, 2) + add_action("create", 3, 0) + add_action("permissive", 3, 1) + add_action("enforce", 3, 2) + add_action("attradd", 4, 0) + add_action("typetrans", 5, 0) + else { fprintf(stderr, "Unknown statement: '%s'\n\n", orig); } + + free(orig); +} + +int magiskpolicy_main(int argc, char *argv[]) { + const char *outfile = nullptr; + bool magisk = false, live = false; + + if (argc < 2) usage(argv[0]); + int i = 1; + for (; i < argc; ++i) { + // Parse options + if (argv[i][0] == '-' && argv[i][1] == '-') { + if (strcmp(argv[i] + 2, "live") == 0) + live = true; + else if (strcmp(argv[i] + 2, "magisk") == 0) + magisk = true; + else if (strcmp(argv[i] + 2, "load") == 0) { + if (argv[i + 1] == nullptr) + usage(argv[0]); + if (load_policydb(argv[i + 1])) { + fprintf(stderr, "Cannot load policy from %s\n", argv[i + 1]); + return 1; + } + ++i; + } else if (strcmp(argv[i] + 2, "compile-split") == 0) { + if (compile_split_cil()) { + fprintf(stderr, "Cannot compile split cil\n"); + return 1; + } + } else if (strcmp(argv[i] + 2, "save") == 0) { + if (argv[i + 1] == nullptr) + usage(argv[0]); + outfile = argv[i + 1]; + ++i; + } else if (strcmp(argv[i] + 2, "help") == 0) { + statements(); + } else { + usage(argv[0]); + } + } else { + break; + } + } + + // Use current policy if nothing is loaded + if (policydb == nullptr && load_policydb(SELINUX_POLICY)) { + fprintf(stderr, "Cannot load policy from " SELINUX_POLICY "\n"); + return 1; + } + + for (; i < argc; ++i) + parse_statement(argv[i]); + + if (magisk) + sepol_magisk_rules(); + + if (live && dump_policydb(SELINUX_LOAD)) { + fprintf(stderr, "Cannot apply policy\n"); + return 1; + } + + if (outfile && dump_policydb(outfile)) { + fprintf(stderr, "Cannot dump policy to %s\n", outfile); + return 1; + } + + destroy_policydb(); + return 0; +} diff --git a/native/jni/magiskpolicy/magiskpolicy.h b/native/jni/magiskpolicy/magiskpolicy.h index a78426d9d..f5f0e8c44 100644 --- a/native/jni/magiskpolicy/magiskpolicy.h +++ b/native/jni/magiskpolicy/magiskpolicy.h @@ -16,6 +16,10 @@ #define SPLIT_PRECOMPILE NONPLAT_POLICY_DIR "precompiled_sepolicy" #define SPLIT_NONPLAT_VER NONPLAT_POLICY_DIR "plat_sepolicy_vers.txt" +#ifdef __cplusplus +extern "C" { +#endif + // policydb functions int load_policydb(const char *filename); int compile_split_cil(); @@ -23,21 +27,25 @@ int dump_policydb(const char *filename); void destroy_policydb(); // Handy functions -int sepol_allow(char *s, char *t, char *c, char *p); -int sepol_deny(char *s, char *t, char *c, char *p); -int sepol_auditallow(char *s, char *t, char *c, char *p); -int sepol_auditdeny(char *s, char *t, char *c, char *p); -int sepol_typetrans(char *s, char *t, char *c, char *d, char *o); -int sepol_allowxperm(char *s, char *t, char *c, char *range); -int sepol_auditallowxperm(char *s, char *t, char *c, char *range); -int sepol_dontauditxperm(char *s, char *t, char *c, char *range); -int sepol_create(char *s); -int sepol_permissive(char *s); -int sepol_enforce(char *s); -int sepol_attradd(char *s, char *a); -int sepol_exists(char *source); +int sepol_allow(const char *s, const char *t, const char *c, const char *p); +int sepol_deny(const char *s, const char *t, const char *c, const char *p); +int sepol_auditallow(const char *s, const char *t, const char *c, const char *p); +int sepol_auditdeny(const char *s, const char *t, const char *c, const char *p); +int sepol_typetrans(const char *s, const char *t, const char *c, const char *d, const char *o); +int sepol_allowxperm(const char *s, const char *t, const char *c, const char *range); +int sepol_auditallowxperm(const char *s, const char *t, const char *c, const char *range); +int sepol_dontauditxperm(const char *s, const char *t, const char *c, const char *range); +int sepol_create(const char *s); +int sepol_permissive(const char *s); +int sepol_enforce(const char *s); +int sepol_attradd(const char *s, const char *a); +int sepol_exists(const char *source); // Built in rules void sepol_magisk_rules(); +#ifdef __cplusplus +}; +#endif + #endif diff --git a/native/jni/magiskpolicy/rules.c b/native/jni/magiskpolicy/rules.cpp similarity index 99% rename from native/jni/magiskpolicy/rules.c rename to native/jni/magiskpolicy/rules.cpp index 5f2747e3c..67c9f1da8 100644 --- a/native/jni/magiskpolicy/rules.c +++ b/native/jni/magiskpolicy/rules.cpp @@ -2,7 +2,7 @@ #include "magiskpolicy.h" #include "sepolicy.h" -static void allowSuClient(char *target) { +static void allowSuClient(const char *target) { if (!sepol_exists(target)) return; sepol_allow(target, SEPOL_PROC_DOMAIN, "unix_stream_socket", "connectto"); diff --git a/native/jni/magiskpolicy/sepolicy.c b/native/jni/magiskpolicy/sepolicy.c index b04f73bdb..57c67d800 100644 --- a/native/jni/magiskpolicy/sepolicy.c +++ b/native/jni/magiskpolicy/sepolicy.c @@ -22,7 +22,6 @@ #include "utils.h" #include "magiskpolicy.h" #include "sepolicy.h" -#include "vector.h" policydb_t *policydb = NULL; extern int policydb_index_decls(sepol_handle_t * handle, policydb_t * p); @@ -36,7 +35,7 @@ static void *cmalloc(size_t s) { return t; } -static int get_attr(char *type, int value) { +static int get_attr(const char *type, int value) { type_datum_t *attr = hashtab_search(policydb->p_types.table, type); if (!attr) return 1; @@ -47,7 +46,7 @@ static int get_attr(char *type, int value) { return !! ebitmap_get_bit(&policydb->attr_type_map[attr->s.value-1], value-1); } -static int get_attr_id(char *type) { +static int get_attr_id(const char *type) { type_datum_t *attr = hashtab_search(policydb->p_types.table, type); if (!attr) return 1; @@ -58,7 +57,7 @@ static int get_attr_id(char *type) { return attr->s.value; } -static int set_attr(char *type, int value) { +static int set_attr(const char *type, int value) { type_datum_t *attr = hashtab_search(policydb->p_types.table, type); if (!attr) return 1; @@ -364,7 +363,7 @@ void destroy_policydb() { policydb = NULL; } -int create_domain(char *d) { +int create_domain(const char *d) { symtab_datum_t *src = hashtab_search(policydb->p_types.table, d); if(src) { fprintf(stderr, "Domain %s already exists\n", d); @@ -414,7 +413,7 @@ int create_domain(char *d) { return set_attr("domain", value); } -int set_domain_state(char* s, int state) { +int set_domain_state(const char *s, int state) { type_datum_t *type; hashtab_ptr_t cur; if (s == NULL) { @@ -440,7 +439,7 @@ int set_domain_state(char* s, int state) { return 0; } -int add_transition(char *s, char *t, char *c, char *d) { +int add_transition(const char *s, const char *t, const char *c, const char *d) { type_datum_t *src, *tgt, *def; class_datum_t *cls; @@ -491,7 +490,8 @@ int add_transition(char *s, char *t, char *c, char *d) { return 0; } -int add_file_transition(char *s, char *t, char *c, char *d, char* filename) { +int add_file_transition(const char *s, const char *t, const char *c, const char *d, + const char *filename) { type_datum_t *src, *tgt, *def; class_datum_t *cls; @@ -520,7 +520,7 @@ int add_file_transition(char *s, char *t, char *c, char *d, char* filename) { trans_key.stype = src->s.value; trans_key.ttype = tgt->s.value; trans_key.tclass = cls->s.value; - trans_key.name = filename; + trans_key.name = (char *) filename; filename_trans_datum_t *trans_datum; trans_datum = hashtab_search(policydb->p_types.table, (hashtab_key_t) &trans_key); @@ -535,7 +535,7 @@ int add_file_transition(char *s, char *t, char *c, char *d, char* filename) { return 0; } -int add_typeattribute(char *domainS, char *attr) { +int add_typeattribute(const char *domainS, const char *attr) { type_datum_t *domain; domain = hashtab_search(policydb->p_types.table, domainS); @@ -564,7 +564,7 @@ int add_typeattribute(char *domainS, char *attr) { return 0; } -int add_rule(char *s, char *t, char *c, char *p, int effect, int not) { +int add_rule(const char *s, const char *t, const char *c, const char *p, int effect, int n) { type_datum_t *src = NULL, *tgt = NULL; class_datum_t *cls = NULL; perm_datum_t *perm = NULL; @@ -610,10 +610,11 @@ int add_rule(char *s, char *t, char *c, char *p, int effect, int not) { } } } - return add_rule_auto(src, tgt, cls, perm, effect, not); + return add_rule_auto(src, tgt, cls, perm, effect, n); } -int add_xperm_rule(char *s, char *t, char *c, char *range, int effect, int not) { +int add_xperm_rule(const char *s, const char *t, const char *c, const char *range, int effect, + int n) { type_datum_t *src = NULL, *tgt = NULL; class_datum_t *cls = NULL; @@ -655,5 +656,5 @@ int add_xperm_rule(char *s, char *t, char *c, char *range, int effect, int not) high = 0xFFFF; } - return add_xperm_rule_auto(src, tgt, cls, low, high, effect, not); + return add_xperm_rule_auto(src, tgt, cls, low, high, effect, n); } diff --git a/native/jni/magiskpolicy/sepolicy.h b/native/jni/magiskpolicy/sepolicy.h index 82c52c3e6..b69a3b583 100644 --- a/native/jni/magiskpolicy/sepolicy.h +++ b/native/jni/magiskpolicy/sepolicy.h @@ -6,6 +6,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + // Global policydb extern policydb_t *policydb; @@ -15,12 +19,17 @@ extern policydb_t *policydb; for (*ptr = table->htable[_i]; *ptr != NULL; *ptr = (*ptr)->next) // sepolicy manipulation functions -int create_domain(char *d); -int set_domain_state(char* s, int state); -int add_transition(char *s, char *t, char *c, char *d); -int add_file_transition(char *s, char *t, char *c, char *d, char* filename); -int add_typeattribute(char *domainS, char *attr); -int add_rule(char *s, char *t, char *c, char *p, int effect, int not); -int add_xperm_rule(char *s, char *t, char *c, char *range, int effect, int not); +int create_domain(const char *d); +int set_domain_state(const char *s, int state); +int add_transition(const char *s, const char *t, const char *c, const char *d); +int add_file_transition(const char *s, const char *t, const char *c, const char *d, + const char *filename); +int add_typeattribute(const char *domainS, const char *attr); +int add_rule(const char *s, const char *t, const char *c, const char *p, int effect, int n); +int add_xperm_rule(const char *s, const char *t, const char *c, const char *range, int effect, int n); + +#ifdef __cplusplus +}; +#endif #endif diff --git a/native/jni/misc/init.c b/native/jni/misc/init.cpp similarity index 95% rename from native/jni/misc/init.c rename to native/jni/misc/init.cpp index cd38f00df..71dce16c5 100644 --- a/native/jni/misc/init.c +++ b/native/jni/misc/init.cpp @@ -1,4 +1,4 @@ -/* init.c - Pre-init Magisk support +/* init.cpp - Pre-init Magisk support * * This code has to be compiled statically to work properly. * @@ -21,7 +21,6 @@ */ -#define _GNU_SOURCE #include #include #include @@ -42,8 +41,9 @@ #include "binaries_arch.h" #include "magiskrc.h" -#include "utils.h" #include "magisk.h" +#include "magiskpolicy.h" +#include "utils.h" #include "flags.h" #define DEFAULT_DT_DIR "/proc/device-tree/firmware/android" @@ -223,9 +223,9 @@ static int patch_sepolicy() { if (init_patch) { // Force init to load /sepolicy - void *addr; + uint8_t *addr; size_t size; - mmap_rw("/init", &addr, &size); + mmap_rw("/init", (void **) &addr, &size); for (int i = 0; i < size; ++i) { if (memcmp(addr + i, SPLIT_PLAT_CIL, sizeof(SPLIT_PLAT_CIL) - 1) == 0) { memcpy(addr + i + sizeof(SPLIT_PLAT_CIL) - 4, "xxx", 3); @@ -238,7 +238,7 @@ static int patch_sepolicy() { return 0; } -static int unxz(int fd, const void *buf, size_t size) { +static int unxz(int fd, const uint8_t *buf, size_t size) { uint8_t out[8192]; xz_crc32_init(); struct xz_dec *dec = xz_dec_init(XZ_DYNALLOC, 1 << 26); @@ -297,10 +297,10 @@ static int dump_magiskrc(const char *path, mode_t mode) { } static void patch_socket_name(const char *path) { - void *buf; + uint8_t *buf; char name[sizeof(MAIN_SOCKET)]; size_t size; - mmap_rw(path, &buf, &size); + mmap_rw(path, (void **) &buf, &size); for (int i = 0; i < size; ++i) { if (memcmp(buf + i, MAIN_SOCKET, sizeof(MAIN_SOCKET)) == 0) { gen_rand_str(name, sizeof(name)); @@ -364,8 +364,8 @@ int main(int argc, char *argv[]) { * ***********/ int root = open("/", O_RDONLY | O_CLOEXEC); - int mnt_system = 0; - int mnt_vendor = 0; + bool mnt_system = false; + bool mnt_vendor = false; if (cmd.skip_initramfs) { // Clear rootfs @@ -407,21 +407,26 @@ int main(int argc, char *argv[]) { } else if (read_fstab_dt(&cmd, "system", partname) == 0) { setup_block(&dev, partname); xmount(dev.path, "/system", "ext4", MS_RDONLY, NULL); - mnt_system = 1; + mnt_system = true; } if (read_fstab_dt(&cmd, "vendor", partname) == 0) { setup_block(&dev, partname); xmount(dev.path, "/vendor", "ext4", MS_RDONLY, NULL); - mnt_vendor = 1; + mnt_vendor = true; } /* **************** * Ramdisk Patches * ****************/ + int fd; + bool injected; + FILE *fp; + char tok[4096]; + // Handle ramdisk overlays - int fd = open("/overlay", O_RDONLY | O_CLOEXEC); + fd = open("/overlay", O_RDONLY | O_CLOEXEC); if (fd >= 0) { mv_dir(fd, root); close(fd); @@ -429,17 +434,17 @@ int main(int argc, char *argv[]) { } // Patch init.rc to load magisk scripts - int injected = 0; - char tok[4096]; - FILE *fp = xfopen("/init.rc", "r"); + + injected = false; + fp = xfopen("/init.rc", "r"); fd = creat("/init.rc.new", 0750); while(fgets(tok, sizeof(tok), fp)) { if (!injected && strncmp(tok, "import", 6) == 0) { if (strstr(tok, "init.magisk.rc")) { - injected = 1; + injected = true; } else { xwrite(fd, "import /init.magisk.rc\n", 23); - injected = 1; + injected = true; } } else if (strstr(tok, "selinux.reload_policy")) { // Do not allow sepolicy patch diff --git a/native/jni/utils/Android.mk b/native/jni/utils/Android.mk index 5a60c3d31..706b06630 100644 --- a/native/jni/utils/Android.mk +++ b/native/jni/utils/Android.mk @@ -9,7 +9,6 @@ LOCAL_SRC_FILES := \ selinux.cpp \ logging.cpp \ xwrap.cpp \ - CharArray.cpp \ - vector.c + CharArray.cpp include $(BUILD_STATIC_LIBRARY) diff --git a/native/jni/utils/include/vector.h b/native/jni/utils/include/vector.h deleted file mode 100644 index 8b1136e58..000000000 --- a/native/jni/utils/include/vector.h +++ /dev/null @@ -1,38 +0,0 @@ -/* vector.h - A simple vector implementation in c - */ - -#ifndef _VECTOR_H_ -#define _VECTOR_H_ - -#include - -struct vector { - unsigned size; - unsigned cap; - void **data; -}; - -void vec_init(struct vector *v); -void vec_push_back(struct vector *v, void *p); -void vec_push_back_all(struct vector *v, void *p, ...); -void *vec_pop_back(struct vector *v); -void vec_sort(struct vector *v, int (*compar)(const void *, const void *)); -void vec_destroy(struct vector *v); -void vec_deep_destroy(struct vector *v); -void vec_dup(struct vector *v, struct vector *vv); - -#define vec_size(v) (v)->size -#define vec_cap(v) (v)->cap -#define vec_entry(v) (v)->data -/* Usage: vec_for_each(vector *v, void *e) */ -#define vec_for_each(v, e) \ - e = v ? (v)->data[0] : NULL; \ - for (int _ = 0; v && _ < (v)->size; ++_, e = (v)->data[_]) - -#define vec_for_each_r(v, e) \ - e = (v && (v)->size > 0) ? (v)->data[(v)->size - 1] : NULL; \ - for (int _ = ((int) (v)->size) - 1; v && _ >= 0; --_, e = (v)->data[_]) - -#define vec_cur(v) vec_entry(v)[_] - -#endif diff --git a/native/jni/utils/vector.c b/native/jni/utils/vector.c deleted file mode 100644 index 70411a17f..000000000 --- a/native/jni/utils/vector.c +++ /dev/null @@ -1,91 +0,0 @@ -/* vector.c - A simple vector implementation in c - */ - -#include -#include -#include - -#include "vector.h" - -void vec_init(struct vector *v) { - if (v == NULL) return; - vec_size(v) = 0; - vec_cap(v) = 1; - vec_entry(v) = malloc(sizeof(void*)); -} - -void vec_push_back(struct vector *v, void *p) { - if (v == NULL) return; - if (vec_size(v) == vec_cap(v)) { - vec_cap(v) *= 2; - vec_entry(v) = realloc(vec_entry(v), sizeof(void*) * vec_cap(v)); - } - vec_entry(v)[vec_size(v)] = p; - ++vec_size(v); -} - -void vec_push_back_all(struct vector *v, void *p, ...) { - va_list argv; - va_start(argv, p); - vec_push_back(v, p); - for (void *arg = va_arg(argv, char*); arg; arg = va_arg(argv, char*)) - vec_push_back(v, arg); - va_end(argv); -} - -void *vec_pop_back(struct vector *v) { - void *ret = vec_entry(v)[vec_size(v) - 1]; - --vec_size(v); - return ret; -} - -static int (*cmp)(const void *, const void *); - -static int vec_comp(const void *a, const void *b) { - void *aa = *((void **)a), *bb = *((void **)b); - if (aa == NULL && bb == NULL) return 0; - else if (aa == NULL) return 1; - else if (bb == NULL) return -1; - else return cmp ? cmp(aa, bb) : 0; -} - -void vec_sort(struct vector *v, int (*compar)(const void *, const void *)) { - if (v == NULL) return; - cmp = compar; - qsort(vec_entry(v), vec_size(v), sizeof(void*), vec_comp); - void *e; - vec_for_each_r(v, e) { - if (e) break; - --vec_size(v); - } -} - -/* Will cleanup only the vector itself - * use in cases when each element requires special cleanup - */ -void vec_destroy(struct vector *v) { - if (v == NULL) return; - vec_size(v) = 0; - vec_cap(v) = 0; - free(vec_entry(v)); - vec_entry(v) = NULL; // Prevent double destroy segfault -} - -/* Will cleanup each element AND the vector itself - * Shall be the general case - */ -void vec_deep_destroy(struct vector *v) { - if (v == NULL) return; - void *e; - vec_for_each(v, e) { - free(e); - } - vec_destroy(v); -} - -void vec_dup(struct vector *v, struct vector *vv) { - vec_size(vv) = vec_size(v); - vec_cap(vv) = vec_cap(v); - vec_entry(vv) = malloc(sizeof(void*) * vec_cap(v)); - memcpy(vec_entry(vv), vec_entry(v), sizeof(void*) * vec_cap(v)); -}