From: Andre Noll Date: Fri, 30 May 2008 13:25:23 +0000 (+0200) Subject: Inplement the --uid option. X-Git-Tag: v0.0.2~36^2~4 X-Git-Url: http://git.tuebingen.mpg.de/?p=adu.git;a=commitdiff_plain;h=5738935ef1e34d1a82e9b4fcdb741f2b06667b96 Inplement the --uid option. This adds a parser for the --uid argument that fills in the array of admissible intervals. In select mode the tables for non-admissible uids are no longer opened. Therefore we can no longer use the ->table pointer of struct user_info to find out whether a slot in the hash table is already used. Introduce the new field ->flags to struct user_info that contains a bit indicating whether the slot in the hash table is used and another bit that tells us whether the uid associated with that slot is admissible. --- diff --git a/adu.c b/adu.c index c98b89e..d43f5fe 100644 --- a/adu.c +++ b/adu.c @@ -13,8 +13,16 @@ DEFINE_ERRLIST; /** Command line and config file options. */ static struct gengetopt_args_info conf; +enum uid_info_flags { + /** whether this slot of the hash table is used. */ + UI_FL_SLOT_USED = 1, + /** whether this uid should be taken into account. */ + UI_FL_ADMISSIBLE = 2, +}; + struct user_info { uint32_t uid; + uint32_t flags; struct osl_table *table; uint64_t files; uint64_t bytes; @@ -22,12 +30,93 @@ struct user_info { struct osl_table_description *desc; }; +/** + * Contains info for each user that owns at least one regular file. + * + * Even users that are not taken into account because of the --uid + * option occupy a slot in this hash table. This allows to find out + * quicky whether a uid is admissible. And yes, this has to be fast. + */ static struct user_info *uid_hash_table; +static inline int ui_used(struct user_info *ui) +{ + return ui->flags & UI_FL_SLOT_USED; +} + +static inline int ui_admissible(struct user_info *ui) +{ + return ui->flags & UI_FL_ADMISSIBLE; +} + +struct uid_range { + uint32_t low; + uint32_t high; +}; + +static struct uid_range *admissible_uids; + +static inline int check_uid_arg(const char *arg, uint32_t *uid) +{ + const uint32_t max = ~0U; + /* + * we need an 64-bit int for string -> uid conversion because strtoll() + * returns a signed value. + */ + int64_t val; + int ret = para_atoi64(arg, &val); + + if (ret < 0) + return ret; + if (val < 0 || val > max) + return -ERRNO_TO_ERROR(EINVAL); + *uid = val; + return 1; +} + +static int parse_uid_range(const char *orig_arg, struct uid_range *ur) +{ + int ret; + char *arg = para_strdup(orig_arg), *p = strchr(arg, '-'); + + if (!p || p == arg) { + if (p == arg) /* -42 */ + p++; + ret = check_uid_arg(p, &ur->high); + if (ret < 0) + goto out; + ur->low = p? 0 : ur->high; + ret = 1; + goto out; + } + /* 42- or 42-4711 */ + *p = '\0'; + p++; + ret = check_uid_arg(arg, &ur->low); + if (ret < 0) + goto out; + ur->high = ~0U; + if (*p) { /* 42-4711 */ + ret = check_uid_arg(p, &ur->high); + if (ret < 0) + goto out; + } + if (ur->low > ur->high) + ret = -ERRNO_TO_ERROR(EINVAL); +out: + if (ret < 0) + ERROR_LOG("bad uid option: %s\n", orig_arg); + else + INFO_LOG("admissible uid range: %u - %u\n", ur->low, + ur->high); + free(arg); + return ret; +} + + /** evaluates to 1 if x < y, to -1 if x > y and to 0 if x == y */ #define NUM_COMPARE(x, y) ((int)((x) < (y)) - (int)((x) > (y))) - /** * The log function. * @@ -263,6 +352,7 @@ err: ui->desc->dir = NULL; ui->desc = NULL; ui->table = NULL; + ui->flags = 0; return ret; } @@ -325,6 +415,22 @@ enum search_uid_flags { CREATE_USER_TABLE = 2, }; +static int uid_is_admissible(uint32_t uid) +{ + int i; + + for (i = 0; i < conf.uid_given; i++) { + struct uid_range *ur = admissible_uids + i; + + if (ur->low <= uid && ur->high >= uid) + break; + } + i = !conf.uid_given || i < conf.uid_given; + DEBUG_LOG("uid %u is %sadmissible\n", (unsigned)uid, + i? "" : "not "); + return i; +} + static int search_uid(uint32_t uid, enum search_uid_flags flags, struct user_info **ui_ptr) { @@ -333,15 +439,19 @@ static int search_uid(uint32_t uid, enum search_uid_flags flags, for (p = 0; p < uid_hash_table_size; p++) { struct user_info *ui = uid_hash_table + double_hash(uid, p); - if (!ui->table) { + if (!ui_used(ui)) { int ret; - if (!flags) return -E_BAD_UID; ui->uid = uid; + ui->flags |= UI_FL_SLOT_USED; + if (!uid_is_admissible(uid)) + return 0; + ui->flags |= UI_FL_ADMISSIBLE; ret = open_user_table(ui, flags & CREATE_USER_TABLE); if (ret < 0) return ret; + if (ui_ptr) *ui_ptr = ui; return 1; @@ -593,7 +703,7 @@ static void print_id_stats(void) FOR_EACH_USER(ui) { char formated_dir_count[25], formated_file_count[25], formated_bytes[25]; - if (!ui->table) + if (!ui_used(ui)) continue; format_count_value(conf.count_unit_arg, ui->dirs, formated_dir_count); @@ -697,13 +807,11 @@ static void print_user_stats(void) .count = conf.limit_arg, .ui = ui }; - if (!ui->table) + if (!ui_used(ui) || !ui_admissible(ui)) continue; usi.flags = USF_PRINT_DIRNAME | USF_PRINT_BYTES | USF_COMPUTE_SUMMARY; printf("************************************************ uid %u\n", (unsigned) ui->uid); - if (!ui->table) - continue; printf("----------------- Largest dirs -------------------\n"); osl_rbtree_loop_reverse(ui->table, UT_BYTES, &usi, user_stats_loop_function); @@ -763,8 +871,9 @@ static int write_uid_list(void) return 0; buf = para_malloc(size); FOR_EACH_USER(ui) { - if (!ui->table) + if (!ui_used(ui) || !ui_admissible(ui)) continue; + DEBUG_LOG("saving uid %u\n", (unsigned) ui->uid); write_u32(buf + count++ * sizeof(uint32_t), ui->uid); } filename = get_uid_list_name(); @@ -798,7 +907,7 @@ static void close_user_table(struct user_info *ui) { int ret; - if (!ui || !ui->table) + if (!ui || !ui_used(ui) || !ui_admissible(ui)) return; ret = osl_close_table(ui->table, OSL_MARK_CLEAN); if (ret < 0) @@ -811,6 +920,7 @@ static void close_user_table(struct user_info *ui) free(ui->desc); ui->desc = NULL; ui->table = NULL; + ui->flags = 0; } static void close_user_tables(void) @@ -887,6 +997,27 @@ static int com_select(void) return 1; } +static int check_args(void) +{ + int i, ret; + + if (!conf.uid_given) + return 0; + + admissible_uids = para_malloc(conf.uid_given * sizeof(*admissible_uids)); + + for (i = 0; i < conf.uid_given; i++) { + ret = parse_uid_range(conf.uid_arg[i], admissible_uids + i); + if (ret < 0) + goto err; + } + return 1; +err: + free(admissible_uids); + admissible_uids = NULL; + return ret; +} + int main(int argc, char **argv) { int ret; @@ -899,6 +1030,9 @@ int main(int argc, char **argv) }; cmdline_parser_ext(argc, argv, &conf, ¶ms); /* aborts on errors */ + ret = check_args(); + if (ret < 0) + goto out; ret = -E_SYNTAX; if (conf.select_given) ret = com_select(); @@ -907,6 +1041,7 @@ int main(int argc, char **argv) if (ret < 0) goto out; out: + free(admissible_uids); if (ret < 0) { ERROR_LOG("%s\n", error_txt(-ret)); return -EXIT_FAILURE;