Mercurial > dovecot > original-hg > dovecot-1.2
view src/plugins/quota/quota-dirsize.c @ 4451:1a35d53c18fc HEAD
Array API redesigned to work using unions. It now provides type safety
without having to enable DEBUG, as long as the compiler supports typeof().
Its API changed a bit. It now allows directly accessing the array contents,
although that's not necessarily recommended. Changed existing array usage to
be type safe in a bit more places. Removed array_t completely. Also did
s/modifyable/modifiable/.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Wed, 28 Jun 2006 16:10:25 +0300 |
parents | 823648215520 |
children | e5e79558ac2d |
line wrap: on
line source
/* Copyright (C) 2005-2006 Timo Sirainen */ /* Quota reporting based on simply summing sizes of all files in mailbox together. */ #include "lib.h" #include "array.h" #include "str.h" #include "quota-private.h" #include <stdlib.h> #include <unistd.h> #include <dirent.h> #include <sys/stat.h> struct dirsize_quota_root { struct quota_root root; uint64_t storage_limit; }; extern struct quota_backend quota_backend_dirsize; static struct quota_root * dirsize_quota_init(struct quota_setup *setup, const char *name) { struct dirsize_quota_root *root; const char *const *args; root = i_new(struct dirsize_quota_root, 1); root->root.name = i_strdup(name); root->root.v = quota_backend_dirsize.v; t_push(); args = t_strsplit(setup->data, ":"); for (; *args != '\0'; args++) { if (strncmp(*args, "storage=", 8) == 0) root->storage_limit = strtoull(*args + 8, NULL, 10); } t_pop(); if (getenv("DEBUG") != NULL) { i_info("dirsize quota limit = %llukB", (unsigned long long)root->storage_limit); } return &root->root; } static void dirsize_quota_deinit(struct quota_root *_root) { struct dirsize_quota_root *root = (struct dirsize_quota_root *)_root; i_free(root->root.name); i_free(root); } static bool dirsize_quota_add_storage(struct quota_root *root __attr_unused__, struct mail_storage *storage __attr_unused__) { return TRUE; } static void dirsize_quota_remove_storage(struct quota_root *root __attr_unused__, struct mail_storage *storage __attr_unused__) { } static const char *const * dirsize_quota_root_get_resources(struct quota_root *root __attr_unused__) { static const char *resources[] = { QUOTA_NAME_STORAGE, NULL }; return resources; } static int get_dir_usage(const char *dir, uint64_t *value) { DIR *dirp; string_t *path; struct dirent *d; struct stat st; unsigned int path_pos; int ret; dirp = opendir(dir); if (dirp == NULL) { if (errno == ENOENT) return 0; i_error("opendir(%s) failed: %m", dir); return -1; } path = t_str_new(128); str_append(path, dir); str_append_c(path, '/'); path_pos = str_len(path); ret = 0; while ((d = readdir(dirp)) != NULL) { if (d->d_name[0] == '.' && (d->d_name[1] == '\0' || (d->d_name[1] == '.' && d->d_name[2] == '\0'))) { /* skip . and .. */ continue; } str_truncate(path, path_pos); str_append(path, d->d_name); if (lstat(str_c(path), &st) < 0) { if (errno == ENOENT) continue; i_error("lstat(%s) failed: %m", dir); ret = -1; break; } else if (S_ISDIR(st.st_mode)) { if (get_dir_usage(str_c(path), value) < 0) { ret = -1; break; } } else { *value += st.st_size; } } (void)closedir(dirp); return ret; } static int get_usage(struct dirsize_quota_root *root, const char *path, bool is_file, uint64_t *value_r) { struct stat st; if (is_file) { if (lstat(path, &st) < 0) { if (errno == ENOENT) return 0; i_error("lstat(%s) failed: %m", path); return -1; } *value_r += st.st_size; } else { if (get_dir_usage(path, value_r) < 0) { quota_set_error(root->root.setup->quota, "Internal quota calculation error"); return -1; } } return 0; } struct quota_count_path { const char *path; bool is_file; }; ARRAY_DEFINE_TYPE(quota_count_path, struct quota_count_path); static void quota_count_path_add(ARRAY_TYPE(quota_count_path) *paths, const char *path, bool is_file) { struct quota_count_path *count_path; unsigned int i, count; count_path = array_get_modifiable(paths, &count); for (i = 0; i < count; i++) { if (strncmp(count_path[i].path, path, strlen(count_path[i].path)) == 0) { /* this path is already being counted */ return; } if (strncmp(count_path[i].path, path, strlen(path)) == 0) { /* the new path contains the existing path */ i_assert(!is_file); count_path += i; break; } } if (i == count) count_path = array_append_space(paths); count_path->path = t_strdup(path); count_path->is_file = is_file; } static int get_quota_root_usage(struct dirsize_quota_root *root, uint64_t *value_r) { struct mail_storage *const *storages; ARRAY_TYPE(quota_count_path) paths; const struct quota_count_path *count_paths; unsigned int i, count; const char *path; bool is_file; t_push(); ARRAY_CREATE(&paths, pool_datastack_create(), struct quota_count_path, 8); storages = array_get(&root->root.storages, &count); for (i = 0; i < count; i++) { path = mail_storage_get_mailbox_path(storages[i], "", &is_file); quota_count_path_add(&paths, path, is_file); /* INBOX may be in different path. */ path = mail_storage_get_mailbox_path(storages[i], "INBOX", &is_file); quota_count_path_add(&paths, path, is_file); } /* now sum up the found paths */ count_paths = array_get(&paths, &count); for (i = 0; i < count; i++) { if (get_usage(root, count_paths[i].path, count_paths[i].is_file, value_r) < 0) { t_pop(); return -1; } } t_pop(); return 0; } static int dirsize_quota_get_resource(struct quota_root *_root, const char *name, uint64_t *value_r, uint64_t *limit_r) { struct dirsize_quota_root *root = (struct dirsize_quota_root *)_root; *value_r = 0; *limit_r = 0; if (strcasecmp(name, QUOTA_NAME_STORAGE) != 0) return 0; if (get_quota_root_usage(root, value_r) < 0) return -1; *value_r /= 1024; *limit_r = root->storage_limit; return 1; } static int dirsize_quota_set_resource(struct quota_root *root, const char *name __attr_unused__, uint64_t value __attr_unused__) { quota_set_error(root->setup->quota, MAIL_STORAGE_ERR_NO_PERMISSION); return -1; } static struct quota_root_transaction_context * dirsize_quota_transaction_begin(struct quota_root *_root, struct quota_transaction_context *_ctx) { struct dirsize_quota_root *root = (struct dirsize_quota_root *)_root; struct quota_root_transaction_context *ctx; ctx = i_new(struct quota_root_transaction_context, 1); ctx->root = _root; ctx->ctx = _ctx; /* Get dir usage only once at the beginning of transaction. When copying/appending lots of mails we don't want to re-read the entire directory structure after each mail. */ if (get_quota_root_usage(root, &ctx->bytes_current) < 0 || ctx->bytes_current == (uint64_t)-1) { ctx->bytes_current = (uint64_t)-1; quota_set_error(_root->setup->quota, "Internal quota calculation error"); } ctx->bytes_limit = root->storage_limit * 1024; ctx->count_limit = (uint64_t)-1; return ctx; } static int dirsize_quota_transaction_commit(struct quota_root_transaction_context *ctx) { int ret = ctx->bytes_current == (uint64_t)-1 ? -1 : 0; i_free(ctx); return ret; } struct quota_backend quota_backend_dirsize = { "dirsize", { dirsize_quota_init, dirsize_quota_deinit, dirsize_quota_add_storage, dirsize_quota_remove_storage, dirsize_quota_root_get_resources, dirsize_quota_get_resource, dirsize_quota_set_resource, dirsize_quota_transaction_begin, dirsize_quota_transaction_commit, quota_default_transaction_rollback, quota_default_try_alloc, quota_default_try_alloc_bytes, quota_default_test_alloc_bytes, quota_default_alloc, quota_default_free } };