quota-dirsize.c revision 9fc97c8aa8190df87624d214bcc5d0b5362bec93
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2005-2012 Dovecot authors, see the included COPYING file */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen/* Quota reporting based on simply summing sizes of all files in mailbox
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen together. */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "lib.h"
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen#include "array.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "str.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "quota-private.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include <stdlib.h>
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include <unistd.h>
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include <dirent.h>
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include <sys/stat.h>
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstruct quota_count_path {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen const char *path;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen bool is_file;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen};
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo SirainenARRAY_DEFINE_TYPE(quota_count_path, struct quota_count_path);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenextern struct quota_backend quota_backend_dirsize;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic struct quota_root *dirsize_quota_alloc(void)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return i_new(struct quota_root, 1);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenstatic void dirsize_quota_deinit(struct quota_root *_root)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_free(_root);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic const char *const *
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainendirsize_quota_root_get_resources(struct quota_root *root ATTR_UNUSED)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen static const char *resources[] = { QUOTA_NAME_STORAGE_KILOBYTES, NULL };
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return resources;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic int get_dir_usage(const char *dir, uint64_t *value)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen DIR *dirp;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen string_t *path;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct dirent *d;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct stat st;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen unsigned int path_pos;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen int ret;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen dirp = opendir(dir);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (dirp == NULL) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (errno == ENOENT)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return 0;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_error("opendir(%s) failed: %m", dir);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return -1;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen path = t_str_new(128);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen str_append(path, dir);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen str_append_c(path, '/');
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen path_pos = str_len(path);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen ret = 0;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen while ((d = readdir(dirp)) != NULL) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (d->d_name[0] == '.' &&
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen (d->d_name[1] == '\0' ||
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen (d->d_name[1] == '.' && d->d_name[2] == '\0'))) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* skip . and .. */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen continue;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen str_truncate(path, path_pos);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen str_append(path, d->d_name);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (lstat(str_c(path), &st) < 0) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (errno == ENOENT)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen continue;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_error("lstat(%s) failed: %m", dir);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen ret = -1;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen break;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen } else if (S_ISDIR(st.st_mode)) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (get_dir_usage(str_c(path), value) < 0) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen ret = -1;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen break;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen } else {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen *value += st.st_size;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen (void)closedir(dirp);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return ret;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int get_usage(const char *path, bool is_file, uint64_t *value_r)
d735048af768fcac51aff26931b9887182a13554Timo Sirainen{
d735048af768fcac51aff26931b9887182a13554Timo Sirainen struct stat st;
d735048af768fcac51aff26931b9887182a13554Timo Sirainen
d735048af768fcac51aff26931b9887182a13554Timo Sirainen if (is_file) {
d735048af768fcac51aff26931b9887182a13554Timo Sirainen if (lstat(path, &st) < 0) {
d735048af768fcac51aff26931b9887182a13554Timo Sirainen if (errno == ENOENT)
d735048af768fcac51aff26931b9887182a13554Timo Sirainen return 0;
d735048af768fcac51aff26931b9887182a13554Timo Sirainen
d735048af768fcac51aff26931b9887182a13554Timo Sirainen i_error("lstat(%s) failed: %m", path);
d735048af768fcac51aff26931b9887182a13554Timo Sirainen return -1;
d735048af768fcac51aff26931b9887182a13554Timo Sirainen }
d735048af768fcac51aff26931b9887182a13554Timo Sirainen *value_r += st.st_size;
d735048af768fcac51aff26931b9887182a13554Timo Sirainen } else {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (get_dir_usage(path, value_r) < 0)
d735048af768fcac51aff26931b9887182a13554Timo Sirainen return -1;
d735048af768fcac51aff26931b9887182a13554Timo Sirainen }
d735048af768fcac51aff26931b9887182a13554Timo Sirainen return 0;
d735048af768fcac51aff26931b9887182a13554Timo Sirainen}
d735048af768fcac51aff26931b9887182a13554Timo Sirainen
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainenstatic void quota_count_path_add(ARRAY_TYPE(quota_count_path) *paths,
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen const char *path, bool is_file)
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen{
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen struct quota_count_path *count_path;
9912076f6653ddfa04f5c4208c1cfc9f83d1dc04Timo Sirainen unsigned int i, count, path_len;
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen
9912076f6653ddfa04f5c4208c1cfc9f83d1dc04Timo Sirainen path_len = strlen(path);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen count_path = array_get_modifiable(paths, &count);
9912076f6653ddfa04f5c4208c1cfc9f83d1dc04Timo Sirainen for (i = 0; i < count; ) {
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen if (strncmp(count_path[i].path, path,
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen strlen(count_path[i].path)) == 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* this path has already been counted */
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen return;
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen }
9912076f6653ddfa04f5c4208c1cfc9f83d1dc04Timo Sirainen if (strncmp(count_path[i].path, path, path_len) == 0 &&
9912076f6653ddfa04f5c4208c1cfc9f83d1dc04Timo Sirainen count_path[i].path[path_len] == '/') {
9912076f6653ddfa04f5c4208c1cfc9f83d1dc04Timo Sirainen /* the new path contains the existing path.
9912076f6653ddfa04f5c4208c1cfc9f83d1dc04Timo Sirainen drop it and see if there are more to drop. */
9912076f6653ddfa04f5c4208c1cfc9f83d1dc04Timo Sirainen array_delete(paths, i, 1);
9912076f6653ddfa04f5c4208c1cfc9f83d1dc04Timo Sirainen count_path = array_get_modifiable(paths, &count);
9912076f6653ddfa04f5c4208c1cfc9f83d1dc04Timo Sirainen } else {
9912076f6653ddfa04f5c4208c1cfc9f83d1dc04Timo Sirainen i++;
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen }
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen }
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen
9912076f6653ddfa04f5c4208c1cfc9f83d1dc04Timo Sirainen count_path = array_append_space(paths);
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen count_path->path = t_strdup(path);
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen count_path->is_file = is_file;
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen}
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic int
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenget_quota_root_usage(struct quota_root *root, uint64_t *value_r)
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen{
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct mail_namespace *const *namespaces;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen ARRAY_TYPE(quota_count_path) paths;
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen const struct quota_count_path *count_paths;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen unsigned int i, count;
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen const char *path;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen bool is_file;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen t_array_init(&paths, 8);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen namespaces = array_get(&root->quota->namespaces, &count);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen for (i = 0; i < count; i++) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (!quota_root_is_namespace_visible(root, namespaces[i]))
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen continue;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen is_file = mail_storage_is_mailbox_file(namespaces[i]->storage);
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (mailbox_list_get_root_path(namespaces[i]->list,
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen MAILBOX_LIST_PATH_TYPE_DIR, &path))
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen quota_count_path_add(&paths, path, FALSE);
d735048af768fcac51aff26931b9887182a13554Timo Sirainen
d735048af768fcac51aff26931b9887182a13554Timo Sirainen /* INBOX may be in different path. */
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (mailbox_list_get_path(namespaces[i]->list, "INBOX",
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen MAILBOX_LIST_PATH_TYPE_MAILBOX, &path) > 0)
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen quota_count_path_add(&paths, path, is_file);
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen }
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen /* now sum up the found paths */
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen *value_r = 0;
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen count_paths = array_get(&paths, &count);
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen for (i = 0; i < count; i++) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (get_usage(count_paths[i].path, count_paths[i].is_file,
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen value_r) < 0)
f632ecfcabd7c701ab2b14f4eacb78117a31109dTimo Sirainen return -1;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen }
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen return 0;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen}
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenstatic int
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainendirsize_quota_get_resource(struct quota_root *_root, const char *name,
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen uint64_t *value_r)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen int ret;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen if (strcasecmp(name, QUOTA_NAME_STORAGE_BYTES) != 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return 0;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen T_BEGIN {
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen ret = get_quota_root_usage(_root, value_r);
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen } T_END;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen return ret < 0 ? -1 : 1;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainendirsize_quota_update(struct quota_root *root ATTR_UNUSED,
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainen struct quota_transaction_context *ctx ATTR_UNUSED)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return 0;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenstruct quota_backend quota_backend_dirsize = {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen "dirsize",
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen dirsize_quota_alloc,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen NULL,
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen dirsize_quota_deinit,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen NULL,
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen NULL,
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainen NULL,
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen dirsize_quota_root_get_resources,
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen dirsize_quota_get_resource,
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen dirsize_quota_update,
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen NULL
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen};