X-Git-Url: http://git.tuebingen.mpg.de/?a=blobdiff_plain;f=adu.c;h=8f5ff8d655b439191108d372459fe15a153ec64d;hb=66dc72e7781e232e7fd166a0d420eb586250a4ea;hp=8068d205f2274dded3074158a105243bb4de1b11;hpb=6c043b44a1cec206f3b4f8063fb8383e66ae6d53;p=adu.git diff --git a/adu.c b/adu.c index 8068d20..8f5ff8d 100644 --- a/adu.c +++ b/adu.c @@ -3,22 +3,26 @@ #include "gcc-compat.h" #include "cmdline.h" -#include "osl.h" #include "fd.h" -#include "hash.h" #include "string.h" #include "error.h" +#include "portable_io.h" DEFINE_ERRLIST; -#define DATABASE_DIR "/tmp/adu" -#define UID_LIST DATABASE_DIR "/" "uid_list" - /** 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; @@ -26,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. * @@ -177,7 +262,6 @@ static struct osl_table_description dir_table_desc = { .num_columns = NUM_DT_COLUMNS, .flags = 0, .column_descriptions = dir_table_cols, - .dir = DATABASE_DIR }; /** The columns of the id table. */ @@ -246,15 +330,15 @@ static int open_user_table(struct user_info *ui, int create) ui->desc->num_columns = NUM_UT_COLUMNS; ui->desc->flags = 0; ui->desc->column_descriptions = user_table_cols; - ui->desc->dir = para_strdup(DATABASE_DIR); + ui->desc->dir = para_strdup(conf.database_dir_arg); ui->desc->name = make_message("%u", (unsigned)ui->uid); - num_uids++; INFO_LOG(".............................uid #%u: %u\n", (unsigned)num_uids, (unsigned)ui->uid); if (create) { ret = osl_create_table(ui->desc); if (ret < 0) goto err; + num_uids++; } ret = osl_open_table(ui->desc, &ui->table); if (ret < 0) @@ -268,6 +352,7 @@ err: ui->desc->dir = NULL; ui->desc = NULL; ui->table = NULL; + ui->flags = 0; return ret; } @@ -292,6 +377,7 @@ static int create_tables(void) { int ret; + dir_table_desc.dir = para_strdup(conf.database_dir_arg); ret = osl_create_table(&dir_table_desc); if (ret < 0) return ret; @@ -312,7 +398,7 @@ static int create_tables(void) * * An odd number is sufficient to make sure each entry of the hash table gets * probed for probe_num between 0 and s-1 because s is a power of two, hence - * the second hash value never hash a common divisor with the hash table size. + * the second hash value has never a common divisor with the hash table size. * IOW: h is invertible in the ring [0..s]. */ static uint32_t double_hash(uint32_t uid, uint32_t probe_num) @@ -329,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) { @@ -337,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; @@ -487,6 +593,42 @@ static int get_dir_name(struct osl_row *row, char **name) return 1; } +const uint64_t size_unit_divisors[] = { + [size_unit_arg_b] = 1ULL, + [size_unit_arg_k] = 1024ULL, + [size_unit_arg_m] = 1024ULL * 1024ULL, + [size_unit_arg_g] = 1024ULL * 1024ULL * 1024ULL, + [size_unit_arg_t] = 1024ULL * 1024ULL * 1024ULL * 1024ULL, +}; + +const uint64_t count_unit_divisors[] = { + + [count_unit_arg_n] = 1ULL, + [count_unit_arg_k] = 1000ULL, + [count_unit_arg_m] = 1000ULL * 1000ULL, + [count_unit_arg_g] = 1000ULL * 1000ULL * 1000ULL, + [count_unit_arg_t] = 1000ULL * 1000ULL * 1000ULL * 1000ULL, +}; + +const char size_unit_abbrevs[] = " BKMGT"; +const char count_unit_abbrevs[] = " KMGT"; + +static void format_size_value(enum enum_size_unit unit, uint64_t value, char *result) +{ + if (unit == size_unit_arg_h) /* human readable */ + for (unit = size_unit_arg_b; unit < size_unit_arg_t && value > size_unit_divisors[unit + 1]; unit++) + ; /* nothing */ + sprintf(result, "%llu%c", (long long unsigned)value / size_unit_divisors[unit], size_unit_abbrevs[unit]); +} + +static void format_count_value(enum enum_count_unit unit, uint64_t value, char *result) +{ + if (unit == count_unit_arg_h) /* human readable */ + for (unit = count_unit_arg_n; unit < count_unit_arg_t && value > count_unit_divisors[unit + 1]; unit++) + ; /* nothing */ + sprintf(result, "%llu%c", (long long unsigned)value / count_unit_divisors[unit], count_unit_abbrevs[unit]); +} + enum global_stats_flags { GSF_PRINT_DIRNAME = 1, GSF_PRINT_BYTES = 2, @@ -503,7 +645,7 @@ static int global_stats_loop_function(struct osl_row *row, void *data) { struct global_stats_info *gsi = data; struct osl_object obj; - char *dirname; + char *dirname, formated_value[25]; int ret, summary = gsi->flags & GSF_COMPUTE_SUMMARY; if (!gsi->count && !summary) @@ -523,9 +665,12 @@ static int global_stats_loop_function(struct osl_row *row, void *data) if (ret < 0) return ret; files = *(uint64_t *)obj.data; - if (gsi->count && (gsi->flags & GSF_PRINT_FILES)) - printf("%llu%s", (long long unsigned)files, + if (gsi->count && (gsi->flags & GSF_PRINT_FILES)) { + format_size_value(conf.size_unit_arg, files, + formated_value); + printf("%s%s", formated_value, (gsi->flags & GSF_PRINT_BYTES)? "\t" : "\n"); + } if (summary) num_files += files; } @@ -535,14 +680,17 @@ static int global_stats_loop_function(struct osl_row *row, void *data) if (ret < 0) return ret; bytes = *(uint64_t *)obj.data; - if (gsi->count && (gsi->flags & GSF_PRINT_BYTES)) - printf("%llu\n", (long long unsigned)bytes); + if (gsi->count && (gsi->flags & GSF_PRINT_BYTES)) { + format_size_value(conf.size_unit_arg, bytes, + formated_value); + printf("%s\n", formated_value); + } if (summary) { num_bytes += bytes; num_dirs++; } } - if (gsi->count) + if (gsi->count > 0) gsi->count--; return 1; } @@ -553,12 +701,21 @@ static void print_id_stats(void) printf("--------------------- user summary (uid/dirs/files/bytes):\n"); FOR_EACH_USER(ui) { - if (!ui->table) + char formated_dir_count[25], formated_file_count[25], + formated_bytes[25]; + if (!ui_used(ui)) continue; - printf("%u\t%llu\t%llu\t%llu\n", (unsigned)ui->uid, - (long long unsigned)ui->dirs, - (long long unsigned)ui->files, - (long long unsigned)ui->bytes); + format_count_value(conf.count_unit_arg, ui->dirs, + formated_dir_count); + format_count_value(conf.count_unit_arg, ui->files, + formated_file_count); + format_size_value(conf.size_unit_arg, ui->bytes, + formated_bytes); + printf("%u\t%s\t%s\t%s\n", (unsigned)ui->uid, + formated_dir_count, + formated_file_count, + formated_bytes + ); } } @@ -581,6 +738,7 @@ static int user_stats_loop_function(struct osl_row *row, void *data) struct osl_row *dir_row; struct osl_object obj; int ret, summary = usi->flags & GSF_COMPUTE_SUMMARY; + char formated_value[25]; if (!usi->count && !summary) return -E_LOOP_COMPLETE; @@ -608,11 +766,13 @@ static int user_stats_loop_function(struct osl_row *row, void *data) if (ret < 0) return ret; files = *(uint64_t *)obj.data; - if (usi->count && (usi->flags & USF_PRINT_FILES)) - printf("%llu%s", - (long long unsigned)files, + if (usi->count && (usi->flags & USF_PRINT_FILES)) { + format_size_value(conf.size_unit_arg, files, + formated_value); + printf("%s%s", formated_value, (usi->flags & USF_PRINT_BYTES)? "\t" : "\n" ); + } if (summary) usi->ui->files += files; } @@ -622,15 +782,18 @@ static int user_stats_loop_function(struct osl_row *row, void *data) if (ret < 0) return ret; bytes = *(uint64_t *)obj.data; - if (usi->count && (usi->flags & USF_PRINT_BYTES)) - printf("%llu\n", (long long unsigned)bytes); + if (usi->count && (usi->flags & USF_PRINT_BYTES)) { + format_size_value(conf.size_unit_arg, bytes, + formated_value); + printf("%s\n", formated_value); + } if (summary) { usi->ui->bytes += bytes; usi->ui->dirs++; } } - if (usi->count) + if (usi->count > 0) usi->count--; return 1; } @@ -641,21 +804,19 @@ static void print_user_stats(void) FOR_EACH_USER(ui) { struct user_stats_info usi = { - .count = 10, + .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); printf("---------- dirs containing most files ------------\n"); - usi.count = 10; + usi.count = conf.limit_arg, usi.flags = USF_PRINT_DIRNAME | USF_PRINT_FILES; osl_rbtree_loop_reverse(ui->table, UT_FILES, &usi, user_stats_loop_function); @@ -666,7 +827,7 @@ static int print_statistics(void) { int ret; struct global_stats_info gsi = { - .count = 10, + .count = conf.limit_arg, .flags = GSF_PRINT_DIRNAME | GSF_PRINT_BYTES | GSF_COMPUTE_SUMMARY }; @@ -675,7 +836,7 @@ static int print_statistics(void) global_stats_loop_function); if (ret < 0 && ret != -E_LOOP_COMPLETE) return ret; - gsi.count = 10; + gsi.count = conf.limit_arg; gsi.flags = GSF_PRINT_DIRNAME | GSF_PRINT_FILES; printf("---------- dirs containing most files ------------\n"); @@ -693,9 +854,14 @@ static int print_statistics(void) return 1; } +static char *get_uid_list_name(void) +{ + return make_message("%s/uid_list", conf.database_dir_arg); +} + static int write_uid_list(void) { - char *buf; + char *buf, *filename; uint32_t count = 0; struct user_info *ui; size_t size = num_uids * sizeof(uint32_t); @@ -705,17 +871,22 @@ 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); } - ret = para_write_file(UID_LIST, buf, size); + filename = get_uid_list_name(); + ret = para_write_file(filename, buf, size); + free(filename); free(buf); return ret; } static int open_dir_table(void) { + if (!dir_table_desc.dir) /* we did not create the table */ + dir_table_desc.dir = para_strdup(conf.database_dir_arg); return osl_open_table(&dir_table_desc, &dir_table); } @@ -728,6 +899,7 @@ static void close_dir_table(void) ret = osl_close_table(dir_table, OSL_MARK_CLEAN); if (ret < 0) ERROR_LOG("failed to close dir table: %s\n", error_txt(-ret)); + free((char *)dir_table_desc.dir); dir_table = NULL; } @@ -735,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) @@ -748,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) @@ -784,14 +957,19 @@ out: static int read_uid_file(void) { - char *map; size_t size; - int ret = mmap_full_file(UID_LIST, O_RDONLY, (void **)&map, &size, NULL); uint32_t n; + char *filename = get_uid_list_name(), *map; + int ret = mmap_full_file(filename, O_RDONLY, (void **)&map, &size, NULL); - if (ret < 0) + if (ret < 0) { + INFO_LOG("failed to map %s\n", filename); + free(filename); return ret; + } num_uids = size / 4; + INFO_LOG("found %u uids in %s\n", (unsigned)num_uids, filename); + free(filename); /* hash table size should be a power of two and larger than the number of uids */ uid_hash_table_size = 4; while (uid_hash_table_size < num_uids) @@ -823,6 +1001,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; @@ -835,6 +1034,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(); @@ -843,6 +1045,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;