quota-fs.c revision 0dffa25d211be541ee3c953b23566a1a990789df
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2005-2016 Dovecot authors, see the included COPYING file */
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen/* Only for reporting filesystem quota */
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen
dfa2201c6ac8ddb2d2798dee15662cfe774e644eMartti Rannanjärvi#include "lib.h"
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen#include "array.h"
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen#include "str.h"
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen#include "hostpid.h"
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen#include "mountpoint.h"
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen#include "quota-private.h"
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen#include "quota-fs.h"
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen#ifdef HAVE_FS_QUOTA
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen#include <stdio.h>
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen#include <fcntl.h>
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen#include <unistd.h>
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen#include <sys/stat.h>
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen#ifdef HAVE_LINUX_DQBLK_XFS_H
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen# include <linux/dqblk_xfs.h>
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen# define HAVE_XFS_QUOTA
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen#elif defined (HAVE_XFS_XQM_H)
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen# include <xfs/xqm.h> /* CentOS 4.x at least uses this */
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen# define HAVE_XFS_QUOTA
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen#endif
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen#ifdef HAVE_RQUOTA
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen# include "rquota.h"
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen# define RQUOTA_GETQUOTA_TIMEOUT_SECS 10
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen#endif
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen#ifndef DEV_BSIZE
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen# ifdef DQBSIZE
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen# define DEV_BSIZE DQBSIZE /* AIX */
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen# else
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen# define DEV_BSIZE 512
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen# endif
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen#endif
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen#ifdef HAVE_STRUCT_DQBLK_CURSPACE
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen# define dqb_curblocks dqb_curspace
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen#endif
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen/* Older sys/quota.h doesn't define _LINUX_QUOTA_VERSION at all, which means
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen it supports only v1 quota */
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen#ifndef _LINUX_QUOTA_VERSION
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen# define _LINUX_QUOTA_VERSION 1
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen#endif
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen#define mount_type_is_nfs(mount) \
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen (strcmp((mount)->type, "nfs") == 0 || \
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen strcmp((mount)->type, "nfs4") == 0)
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainenstruct fs_quota_mountpoint {
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen int refcount;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen char *mount_path;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen char *device_path;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen char *type;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen unsigned int block_size;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen#ifdef FS_QUOTA_SOLARIS
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen int fd;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen char *path;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen#endif
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen};
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainenstruct fs_quota_root {
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen struct quota_root root;
74ec4fe074dfc487e9a5b98ff237391c082eceb1Timo Sirainen char *storage_mount_path;
74ec4fe074dfc487e9a5b98ff237391c082eceb1Timo Sirainen
74ec4fe074dfc487e9a5b98ff237391c082eceb1Timo Sirainen uid_t uid;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen gid_t gid;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen struct fs_quota_mountpoint *mount;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen bool inode_per_mail:1;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen bool user_disabled:1;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen bool group_disabled:1;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen#ifdef FS_QUOTA_NETBSD
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen struct quotahandle *qh;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen#endif
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen};
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainenextern struct quota_backend quota_backend_fs;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainenstatic struct quota_root *fs_quota_alloc(void)
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen{
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen struct fs_quota_root *root;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen root = i_new(struct fs_quota_root, 1);
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen root->uid = geteuid();
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen root->gid = getegid();
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen return &root->root;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen}
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainenstatic int fs_quota_init(struct quota_root *_root, const char *args,
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen const char **error_r)
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen{
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen struct fs_quota_root *root = (struct fs_quota_root *)_root;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen const char *const *tmp;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen if (args == NULL)
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen return 0;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen for (tmp = t_strsplit(args, ":"); *tmp != NULL; tmp++) {
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen if (strcmp(*tmp, "user") == 0)
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen root->group_disabled = TRUE;
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen else if (strcmp(*tmp, "group") == 0)
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen root->user_disabled = TRUE;
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen else if (strcmp(*tmp, "inode_per_mail") == 0)
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen root->inode_per_mail = TRUE;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen else if (strcmp(*tmp, "noenforcing") == 0)
23bd9fccb78de27232c48c8b1d75ef2145d51dedTimo Sirainen _root->no_enforcing = TRUE;
23bd9fccb78de27232c48c8b1d75ef2145d51dedTimo Sirainen else if (strcmp(*tmp, "hidden") == 0)
4dfaf598d6f2539caaab7ff0dd51d24a20928db8Timo Sirainen _root->hidden = TRUE;
23bd9fccb78de27232c48c8b1d75ef2145d51dedTimo Sirainen else if (strncmp(*tmp, "mount=", 6) == 0) {
23bd9fccb78de27232c48c8b1d75ef2145d51dedTimo Sirainen i_free(root->storage_mount_path);
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen root->storage_mount_path = i_strdup(*tmp + 6);
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen } else {
4dfaf598d6f2539caaab7ff0dd51d24a20928db8Timo Sirainen *error_r = t_strdup_printf("Invalid parameter: %s", *tmp);
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen return -1;
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen }
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen }
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen _root->auto_updating = TRUE;
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen return 0;
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen}
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainenstatic void fs_quota_mountpoint_free(struct fs_quota_mountpoint *mount)
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen{
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen if (--mount->refcount > 0)
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen return;
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen#ifdef FS_QUOTA_SOLARIS
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen if (mount->fd != -1) {
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen if (close(mount->fd) < 0)
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen i_error("close(%s) failed: %m", mount->path);
4dfaf598d6f2539caaab7ff0dd51d24a20928db8Timo Sirainen }
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen i_free(mount->path);
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen#endif
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen i_free(mount->device_path);
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen i_free(mount->mount_path);
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen i_free(mount->type);
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen i_free(mount);
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen}
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainenstatic void fs_quota_deinit(struct quota_root *_root)
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen{
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen struct fs_quota_root *root = (struct fs_quota_root *)_root;
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen if (root->mount != NULL)
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen fs_quota_mountpoint_free(root->mount);
7ca397e910d2b267bcfaecbcdf9b23523c639776Timo Sirainen i_free(root->storage_mount_path);
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen i_free(root);
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen}
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainenstatic struct fs_quota_mountpoint *fs_quota_mountpoint_get(const char *dir)
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen{
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen struct fs_quota_mountpoint *mount;
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen struct mountpoint point;
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen int ret;
761c440316cc003817b3375b627287eceb6f6dceTimo Sirainen
761c440316cc003817b3375b627287eceb6f6dceTimo Sirainen ret = mountpoint_get(dir, default_pool, &point);
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen if (ret <= 0)
761c440316cc003817b3375b627287eceb6f6dceTimo Sirainen return NULL;
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen
0471a9dde7b4fb55779261460c973f85088b4b0cTimo Sirainen mount = i_new(struct fs_quota_mountpoint, 1);
761c440316cc003817b3375b627287eceb6f6dceTimo Sirainen mount->refcount = 1;
761c440316cc003817b3375b627287eceb6f6dceTimo Sirainen mount->device_path = point.device_path;
761c440316cc003817b3375b627287eceb6f6dceTimo Sirainen mount->mount_path = point.mount_path;
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen mount->type = point.type;
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen mount->block_size = point.block_size;
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen#ifdef FS_QUOTA_SOLARIS
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen mount->fd = -1;
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen#endif
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen
f3ef6fdc790caf67352a07829bedb117bc56fb78Timo Sirainen if (mount_type_is_nfs(mount)) {
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen if (strchr(mount->device_path, ':') == NULL) {
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen i_error("quota-fs: %s is not a valid NFS device path",
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen mount->device_path);
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen fs_quota_mountpoint_free(mount);
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen return NULL;
7ca397e910d2b267bcfaecbcdf9b23523c639776Timo Sirainen }
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen }
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen return mount;
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen}
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen#define QUOTA_ROOT_MATCH(root, mount) \
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen ((root)->root.backend.name == quota_backend_fs.name && \
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen ((root)->storage_mount_path == NULL || \
f3ef6fdc790caf67352a07829bedb117bc56fb78Timo Sirainen strcmp((root)->storage_mount_path, (mount)->mount_path) == 0))
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainenstatic struct fs_quota_root *
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainenfs_quota_root_find_mountpoint(struct quota *quota,
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen const struct fs_quota_mountpoint *mount)
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen{
7ca397e910d2b267bcfaecbcdf9b23523c639776Timo Sirainen struct quota_root *const *roots;
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen struct fs_quota_root *empty = NULL;
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen unsigned int i, count;
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen roots = array_get(&quota->roots, &count);
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen for (i = 0; i < count; i++) {
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen struct fs_quota_root *root = (struct fs_quota_root *)roots[i];
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen if (QUOTA_ROOT_MATCH(root, mount)) {
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen if (root->mount == NULL)
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen empty = root;
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen else if (strcmp(root->mount->mount_path,
882eb225340a9fae014c22b7ac4118f1580e2d60Timo Sirainen mount->mount_path) == 0)
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen return root;
882eb225340a9fae014c22b7ac4118f1580e2d60Timo Sirainen }
882eb225340a9fae014c22b7ac4118f1580e2d60Timo Sirainen }
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen return empty;
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen}
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainenstatic void
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainenfs_quota_mount_init(struct fs_quota_root *root,
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen struct fs_quota_mountpoint *mount, const char *dir)
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen{
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen struct quota_root *const *roots;
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen unsigned int i, count;
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen#ifdef FS_QUOTA_SOLARIS
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen#ifdef HAVE_RQUOTA
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen if (mount_type_is_nfs(mount)) {
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen /* using rquota for this mount */
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen } else
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen#endif
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen if (mount->path == NULL) {
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen mount->path = i_strconcat(mount->mount_path, "/quotas", NULL);
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen mount->fd = open(mount->path, O_RDONLY);
5e9a39da0a9aba60b50e2c1d401f102703431b73Timo Sirainen if (mount->fd == -1 && errno != ENOENT)
826d9b7a1dec011e4777b334af5f4dc4feebb64bTimo Sirainen i_error("open(%s) failed: %m", mount->path);
826d9b7a1dec011e4777b334af5f4dc4feebb64bTimo Sirainen }
826d9b7a1dec011e4777b334af5f4dc4feebb64bTimo Sirainen#endif
826d9b7a1dec011e4777b334af5f4dc4feebb64bTimo Sirainen root->mount = mount;
93e742e81653922361ce61f38319bd53e9bcad91Timo Sirainen
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen if (root->root.quota->set->debug) {
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen i_debug("fs quota add mailbox dir = %s", dir);
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen i_debug("fs quota block device = %s", mount->device_path);
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen i_debug("fs quota mount point = %s", mount->mount_path);
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen i_debug("fs quota mount type = %s", mount->type);
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen }
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen /* if there are more unused quota roots, copy this mount to them */
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen roots = array_get(&root->root.quota->roots, &count);
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen for (i = 0; i < count; i++) {
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen root = (struct fs_quota_root *)roots[i];
49dc101839b5f37a1a3c000421796f162e0017d9Timo Sirainen if (QUOTA_ROOT_MATCH(root, mount) && root->mount == NULL) {
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen mount->refcount++;
49dc101839b5f37a1a3c000421796f162e0017d9Timo Sirainen root->mount = mount;
80b837ed54521cbd241110c2c6b58c02d187b818Timo Sirainen }
80b837ed54521cbd241110c2c6b58c02d187b818Timo Sirainen }
b38d0e738ef8be0c437106ba7967a4bdf8057d32Timo Sirainen}
7ca397e910d2b267bcfaecbcdf9b23523c639776Timo Sirainen
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainenstatic void fs_quota_add_missing_mounts(struct quota *quota)
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen{
a497e8b94b55e035a1ebd28cf1368a8869ddaa62Timo Sirainen struct fs_quota_mountpoint *mount;
7ca397e910d2b267bcfaecbcdf9b23523c639776Timo Sirainen struct quota_root *const *roots;
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen unsigned int i, count;
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen roots = array_get(&quota->roots, &count);
49dc101839b5f37a1a3c000421796f162e0017d9Timo Sirainen for (i = 0; i < count; i++) {
37c4359391810bc5bab0d2c72d7d25d8335b2858Timo Sirainen struct fs_quota_root *root = (struct fs_quota_root *)roots[i];
7ca397e910d2b267bcfaecbcdf9b23523c639776Timo Sirainen
7ca397e910d2b267bcfaecbcdf9b23523c639776Timo Sirainen if (root->root.backend.name != quota_backend_fs.name ||
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen root->storage_mount_path == NULL || root->mount != NULL)
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen continue;
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen mount = fs_quota_mountpoint_get(root->storage_mount_path);
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen if (mount != NULL) {
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen fs_quota_mount_init(root, mount,
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen root->storage_mount_path);
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen }
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen }
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen}
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainenstatic void fs_quota_namespace_added(struct quota *quota,
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen struct mail_namespace *ns)
5cdd1691e5185ecfe424f5de7b6f697813b88ba2Timo Sirainen{
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen struct fs_quota_mountpoint *mount;
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen struct fs_quota_root *root;
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen const char *dir;
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen if (!mailbox_list_get_root_path(ns->list, MAILBOX_LIST_PATH_TYPE_MAILBOX,
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen &dir))
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen mount = NULL;
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen else
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen mount = fs_quota_mountpoint_get(dir);
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen if (mount != NULL) {
4a3e6e01ea368e9a90dc32abc40aea46fe93f926Timo Sirainen root = fs_quota_root_find_mountpoint(quota, mount);
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen if (root != NULL && root->mount == NULL)
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen fs_quota_mount_init(root, mount, dir);
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen else
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen fs_quota_mountpoint_free(mount);
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen }
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen /* we would actually want to do this only once after all quota roots
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen are created, but there's no way to do this right now */
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen fs_quota_add_missing_mounts(quota);
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen}
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen
0622f54c063d2dca88f6b95daa6822cb386068caTimo Sirainenstatic const char *const *
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainenfs_quota_root_get_resources(struct quota_root *_root)
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen{
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen struct fs_quota_root *root = (struct fs_quota_root *)_root;
static const char *resources_kb[] = {
QUOTA_NAME_STORAGE_KILOBYTES,
NULL
};
static const char *resources_kb_messages[] = {
QUOTA_NAME_STORAGE_KILOBYTES,
QUOTA_NAME_MESSAGES,
NULL
};
return root->inode_per_mail ? resources_kb_messages : resources_kb;
}
#if defined(FS_QUOTA_LINUX) || defined(FS_QUOTA_BSDAIX) || \
defined(FS_QUOTA_NETBSD) || defined(HAVE_RQUOTA)
static void fs_quota_root_disable(struct fs_quota_root *root, bool group)
{
if (group)
root->group_disabled = TRUE;
else
root->user_disabled = TRUE;
}
#endif
#ifdef HAVE_RQUOTA
static void
rquota_get_result(const rquota *rq,
uint64_t *bytes_value_r, uint64_t *bytes_limit_r,
uint64_t *count_value_r, uint64_t *count_limit_r)
{
/* use soft limits if they exist, fallback to hard limits */
/* convert the results from blocks to bytes */
*bytes_value_r = (uint64_t)rq->rq_curblocks *
(uint64_t)rq->rq_bsize;
if (rq->rq_bsoftlimit != 0) {
*bytes_limit_r = (uint64_t)rq->rq_bsoftlimit *
(uint64_t)rq->rq_bsize;
} else {
*bytes_limit_r = (uint64_t)rq->rq_bhardlimit *
(uint64_t)rq->rq_bsize;
}
*count_value_r = rq->rq_curfiles;
if (rq->rq_fsoftlimit != 0)
*count_limit_r = rq->rq_fsoftlimit;
else
*count_limit_r = rq->rq_fhardlimit;
}
static int
do_rquota_user(struct fs_quota_root *root,
uint64_t *bytes_value_r, uint64_t *bytes_limit_r,
uint64_t *count_value_r, uint64_t *count_limit_r)
{
struct getquota_rslt result;
struct getquota_args args;
struct timeval timeout;
enum clnt_stat call_status;
CLIENT *cl;
struct fs_quota_mountpoint *mount = root->mount;
const char *host;
char *path;
path = strchr(mount->device_path, ':');
i_assert(path != NULL);
host = t_strdup_until(mount->device_path, path);
path++;
/* For NFSv4, we send the filesystem path without initial /. Server
prepends proper NFS pseudoroot automatically and uses this for
detection of NFSv4 mounts. */
if (strcmp(root->mount->type, "nfs4") == 0) {
while (*path == '/')
path++;
}
if (root->root.quota->set->debug) {
i_debug("quota-fs: host=%s, path=%s, uid=%s",
host, path, dec2str(root->uid));
}
/* clnt_create() polls for a while to establish a connection */
cl = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp");
if (cl == NULL) {
i_error("quota-fs: could not contact RPC service on %s",
host);
return -1;
}
/* Establish some RPC credentials */
auth_destroy(cl->cl_auth);
cl->cl_auth = authunix_create_default();
/* make the rquota call on the remote host */
args.gqa_pathp = path;
args.gqa_uid = root->uid;
timeout.tv_sec = RQUOTA_GETQUOTA_TIMEOUT_SECS;
timeout.tv_usec = 0;
call_status = clnt_call(cl, RQUOTAPROC_GETQUOTA,
(xdrproc_t)xdr_getquota_args, (char *)&args,
(xdrproc_t)xdr_getquota_rslt, (char *)&result,
timeout);
/* the result has been deserialized, let the client go */
auth_destroy(cl->cl_auth);
clnt_destroy(cl);
if (call_status != RPC_SUCCESS) {
const char *rpc_error_msg = clnt_sperrno(call_status);
i_error("quota-fs: remote rquota call failed: %s",
rpc_error_msg);
return -1;
}
switch (result.status) {
case Q_OK: {
rquota_get_result(&result.getquota_rslt_u.gqr_rquota,
bytes_value_r, bytes_limit_r,
count_value_r, count_limit_r);
if (root->root.quota->set->debug) {
i_debug("quota-fs: uid=%s, bytes=%llu/%llu files=%llu/%llu",
dec2str(root->uid),
(unsigned long long)*bytes_value_r,
(unsigned long long)*bytes_limit_r,
(unsigned long long)*count_value_r,
(unsigned long long)*count_limit_r);
}
return 1;
}
case Q_NOQUOTA:
if (root->root.quota->set->debug) {
i_debug("quota-fs: uid=%s, limit=unlimited",
dec2str(root->uid));
}
fs_quota_root_disable(root, FALSE);
return 0;
case Q_EPERM:
i_error("quota-fs: permission denied to rquota service");
return -1;
default:
i_error("quota-fs: unrecognized status code (%d) "
"from rquota service", result.status);
return -1;
}
}
static int
do_rquota_group(struct fs_quota_root *root ATTR_UNUSED,
uint64_t *bytes_value_r ATTR_UNUSED,
uint64_t *bytes_limit_r ATTR_UNUSED,
uint64_t *count_value_r ATTR_UNUSED,
uint64_t *count_limit_r ATTR_UNUSED)
{
#if defined(EXT_RQUOTAVERS) && defined(GRPQUOTA)
struct getquota_rslt result;
ext_getquota_args args;
struct timeval timeout;
enum clnt_stat call_status;
CLIENT *cl;
struct fs_quota_mountpoint *mount = root->mount;
const char *host;
char *path;
path = strchr(mount->device_path, ':');
i_assert(path != NULL);
host = t_strdup_until(mount->device_path, path);
path++;
if (root->root.quota->set->debug) {
i_debug("quota-fs: host=%s, path=%s, gid=%s",
host, path, dec2str(root->gid));
}
/* clnt_create() polls for a while to establish a connection */
cl = clnt_create(host, RQUOTAPROG, EXT_RQUOTAVERS, "udp");
if (cl == NULL) {
i_error("quota-fs: could not contact RPC service on %s (group)",
host);
return -1;
}
/* Establish some RPC credentials */
auth_destroy(cl->cl_auth);
cl->cl_auth = authunix_create_default();
/* make the rquota call on the remote host */
args.gqa_pathp = path;
args.gqa_id = root->gid;
args.gqa_type = GRPQUOTA;
timeout.tv_sec = RQUOTA_GETQUOTA_TIMEOUT_SECS;
timeout.tv_usec = 0;
call_status = clnt_call(cl, RQUOTAPROC_GETQUOTA,
(xdrproc_t)xdr_ext_getquota_args, (char *)&args,
(xdrproc_t)xdr_getquota_rslt, (char *)&result,
timeout);
/* the result has been deserialized, let the client go */
auth_destroy(cl->cl_auth);
clnt_destroy(cl);
if (call_status != RPC_SUCCESS) {
const char *rpc_error_msg = clnt_sperrno(call_status);
i_error("quota-fs: remote ext rquota call failed: %s",
rpc_error_msg);
return -1;
}
switch (result.status) {
case Q_OK: {
rquota_get_result(&result.getquota_rslt_u.gqr_rquota,
bytes_value_r, bytes_limit_r,
count_value_r, count_limit_r);
if (root->root.quota->set->debug) {
i_debug("quota-fs: gid=%s, bytes=%llu/%llu files=%llu/%llu",
dec2str(root->gid),
(unsigned long long)*bytes_value_r,
(unsigned long long)*bytes_limit_r,
(unsigned long long)*count_value_r,
(unsigned long long)*count_limit_r);
}
return 1;
}
case Q_NOQUOTA:
if (root->root.quota->set->debug) {
i_debug("quota-fs: gid=%s, limit=unlimited",
dec2str(root->gid));
}
fs_quota_root_disable(root, TRUE);
return 0;
case Q_EPERM:
i_error("quota-fs: permission denied to ext rquota service");
return -1;
default:
i_error("quota-fs: unrecognized status code (%d) "
"from ext rquota service", result.status);
return -1;
}
#else
i_error("quota-fs: rquota not compiled with group support");
return -1;
#endif
}
#endif
#ifdef FS_QUOTA_LINUX
static int
fs_quota_get_linux(struct fs_quota_root *root, bool group,
uint64_t *bytes_value_r, uint64_t *bytes_limit_r,
uint64_t *count_value_r, uint64_t *count_limit_r)
{
struct dqblk dqblk;
int type, id;
type = group ? GRPQUOTA : USRQUOTA;
id = group ? root->gid : root->uid;
#ifdef HAVE_XFS_QUOTA
if (strcmp(root->mount->type, "xfs") == 0) {
struct fs_disk_quota xdqblk;
if (quotactl(QCMD(Q_XGETQUOTA, type),
root->mount->device_path,
id, (caddr_t)&xdqblk) < 0) {
if (errno == ESRCH) {
fs_quota_root_disable(root, group);
return 0;
}
i_error("%d quotactl(Q_XGETQUOTA, %s) failed: %m",
errno, root->mount->device_path);
return -1;
}
/* values always returned in 512 byte blocks */
*bytes_value_r = xdqblk.d_bcount * 512;
*bytes_limit_r = xdqblk.d_blk_softlimit * 512;
if (*bytes_limit_r == 0) {
*bytes_limit_r = xdqblk.d_blk_hardlimit * 512;
}
*count_value_r = xdqblk.d_icount;
*count_limit_r = xdqblk.d_ino_softlimit;
if (*count_limit_r == 0) {
*count_limit_r = xdqblk.d_ino_hardlimit;
}
} else
#endif
{
/* ext2, ext3 */
if (quotactl(QCMD(Q_GETQUOTA, type),
root->mount->device_path,
id, (caddr_t)&dqblk) < 0) {
if (errno == ESRCH) {
fs_quota_root_disable(root, group);
return 0;
}
i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
root->mount->device_path);
if (errno == EINVAL) {
i_error("Dovecot was compiled with Linux quota "
"v%d support, try changing it "
"(CPPFLAGS=-D_LINUX_QUOTA_VERSION=%d configure)",
_LINUX_QUOTA_VERSION,
_LINUX_QUOTA_VERSION == 1 ? 2 : 1);
}
return -1;
}
#if _LINUX_QUOTA_VERSION == 1
*bytes_value_r = dqblk.dqb_curblocks * 1024;
#else
*bytes_value_r = dqblk.dqb_curblocks;
#endif
*bytes_limit_r = dqblk.dqb_bsoftlimit * 1024;
if (*bytes_limit_r == 0) {
*bytes_limit_r = dqblk.dqb_bhardlimit * 1024;
}
*count_value_r = dqblk.dqb_curinodes;
*count_limit_r = dqblk.dqb_isoftlimit;
if (*count_limit_r == 0) {
*count_limit_r = dqblk.dqb_ihardlimit;
}
}
return 1;
}
#endif
#ifdef FS_QUOTA_BSDAIX
static int
fs_quota_get_bsdaix(struct fs_quota_root *root, bool group,
uint64_t *bytes_value_r, uint64_t *bytes_limit_r,
uint64_t *count_value_r, uint64_t *count_limit_r)
{
struct dqblk dqblk;
int type, id;
type = group ? GRPQUOTA : USRQUOTA;
id = group ? root->gid : root->uid;
if (quotactl(root->mount->mount_path, QCMD(Q_GETQUOTA, type),
id, (void *)&dqblk) < 0) {
if (errno == ESRCH) {
fs_quota_root_disable(root, group);
return 0;
}
i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
root->mount->mount_path);
return -1;
}
*bytes_value_r = (uint64_t)dqblk.dqb_curblocks * DEV_BSIZE;
*bytes_limit_r = (uint64_t)dqblk.dqb_bsoftlimit * DEV_BSIZE;
if (*bytes_limit_r == 0) {
*bytes_limit_r = (uint64_t)dqblk.dqb_bhardlimit * DEV_BSIZE;
}
*count_value_r = dqblk.dqb_curinodes;
*count_limit_r = dqblk.dqb_isoftlimit;
if (*count_limit_r == 0) {
*count_limit_r = dqblk.dqb_ihardlimit;
}
return 1;
}
#endif
#ifdef FS_QUOTA_NETBSD
static int
fs_quota_get_netbsd(struct fs_quota_root *root, bool group,
uint64_t *bytes_value_r, uint64_t *bytes_limit_r,
uint64_t *count_value_r, uint64_t *count_limit_r)
{
struct quotakey qk;
struct quotaval qv;
struct quotahandle *qh;
int ret;
if ((qh = quota_open(root->mount->mount_path)) == NULL) {
i_error("cannot open quota for %s: %m",
root->mount->mount_path);
fs_quota_root_disable(root, group);
return 0;
}
qk.qk_idtype = group ? QUOTA_IDTYPE_GROUP : QUOTA_IDTYPE_USER;
qk.qk_id = group ? root->gid : root->uid;
for (int i = 0; i < 2; i++) {
qk.qk_objtype = i == 0 ? QUOTA_OBJTYPE_BLOCKS : QUOTA_OBJTYPE_FILES;
if (quota_get(qh, &qk, &qv) != 0) {
if (errno == ESRCH) {
fs_quota_root_disable(root, group);
return 0;
}
i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
root->mount->mount_path);
ret = -1;
break;
}
if (i == 0) {
*bytes_value_r = qv.qv_usage * DEV_BSIZE;
*bytes_limit_r = qv.qv_softlimit * DEV_BSIZE;
} else {
*count_value_r = qv.qv_usage;
*count_limit_r = qv.qv_softlimit;
}
ret = 1;
}
quota_close(qh);
return ret;
}
#endif
#ifdef FS_QUOTA_HPUX
static int
fs_quota_get_hpux(struct fs_quota_root *root,
uint64_t *bytes_value_r, uint64_t *bytes_limit_r,
uint64_t *count_value_r, uint64_t *count_limit_r)
{
struct dqblk dqblk;
if (quotactl(Q_GETQUOTA, root->mount->device_path,
root->uid, &dqblk) < 0) {
if (errno == ESRCH) {
root->user_disabled = TRUE;
return 0;
}
i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
root->mount->device_path);
return -1;
}
*bytes_value_r = (uint64_t)dqblk.dqb_curblocks *
root->mount->block_size;
*bytes_limit_r = (uint64_t)dqblk.dqb_bsoftlimit *
root->mount->block_size;
if (*bytes_limit_r == 0) {
*bytes_limit_r = (uint64_t)dqblk.dqb_bhardlimit *
root->mount->block_size;
}
*count_value_r = dqblk.dqb_curfiles;
*count_limit_r = dqblk.dqb_fsoftlimit;
if (*count_limit_r == 0) {
*count_limit_r = dqblk.dqb_fhardlimit;
}
return 1;
}
#endif
#ifdef FS_QUOTA_SOLARIS
static int
fs_quota_get_solaris(struct fs_quota_root *root,
uint64_t *bytes_value_r, uint64_t *bytes_limit_r,
uint64_t *count_value_r, uint64_t *count_limit_r)
{
struct dqblk dqblk;
struct quotctl ctl;
if (root->mount->fd == -1)
return 0;
ctl.op = Q_GETQUOTA;
ctl.uid = root->uid;
ctl.addr = (caddr_t)&dqblk;
if (ioctl(root->mount->fd, Q_QUOTACTL, &ctl) < 0) {
i_error("ioctl(%s, Q_QUOTACTL) failed: %m", root->mount->path);
return -1;
}
*bytes_value_r = (uint64_t)dqblk.dqb_curblocks * DEV_BSIZE;
*bytes_limit_r = (uint64_t)dqblk.dqb_bsoftlimit * DEV_BSIZE;
if (*bytes_limit_r == 0) {
*bytes_limit_r = (uint64_t)dqblk.dqb_bhardlimit * DEV_BSIZE;
}
*count_value_r = dqblk.dqb_curfiles;
*count_limit_r = dqblk.dqb_fsoftlimit;
if (*count_limit_r == 0) {
*count_limit_r = dqblk.dqb_fhardlimit;
}
return 1;
}
#endif
static int
fs_quota_get_resources(struct fs_quota_root *root, bool group,
uint64_t *bytes_value_r, uint64_t *bytes_limit_r,
uint64_t *count_value_r, uint64_t *count_limit_r)
{
if (group) {
if (root->group_disabled)
return 0;
} else {
if (root->user_disabled)
return 0;
}
#ifdef FS_QUOTA_LINUX
return fs_quota_get_linux(root, group, bytes_value_r, bytes_limit_r, count_value_r, count_limit_r);
#elif defined (FS_QUOTA_NETBSD)
return fs_quota_get_netbsd(root, group, bytes_value_r, bytes_limit_r, count_value_r, count_limit_r);
#elif defined (FS_QUOTA_BSDAIX)
return fs_quota_get_bsdaix(root, group, bytes_value_r, bytes_limit_r, count_value_r, count_limit_r);
#else
if (group) {
/* not supported */
return 0;
}
#ifdef FS_QUOTA_HPUX
return fs_quota_get_hpux(root, bytes_value_r, bytes_limit_r, count_value_r, count_limit_r);
#else
return fs_quota_get_solaris(root, bytes_value_r, bytes_limit_r, count_value_r, count_limit_r);
#endif
#endif
}
static bool fs_quota_match_box(struct quota_root *_root, struct mailbox *box)
{
struct fs_quota_root *root = (struct fs_quota_root *)_root;
struct stat mst, rst;
const char *mailbox_path;
bool match;
if (root->storage_mount_path == NULL)
return TRUE;
if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_MAILBOX,
&mailbox_path) <= 0)
return FALSE;
if (stat(mailbox_path, &mst) < 0) {
if (errno != ENOENT)
i_error("stat(%s) failed: %m", mailbox_path);
return FALSE;
}
if (stat(root->storage_mount_path, &rst) < 0) {
if (_root->quota->set->debug) {
i_debug("stat(%s) failed: %m",
root->storage_mount_path);
}
return FALSE;
}
match = CMP_DEV_T(mst.st_dev, rst.st_dev);
if (_root->quota->set->debug) {
i_debug("box=%s mount=%s match=%s", mailbox_path,
root->storage_mount_path, match ? "yes" : "no");
}
return match;
}
static int
fs_quota_get_resource(struct quota_root *_root, const char *name,
uint64_t *value_r)
{
struct fs_quota_root *root = (struct fs_quota_root *)_root;
uint64_t bytes_value, count_value;
uint64_t bytes_limit = 0, count_limit = 0;
int ret;
*value_r = 0;
if (root->mount == NULL ||
(strcasecmp(name, QUOTA_NAME_STORAGE_BYTES) != 0 &&
strcasecmp(name, QUOTA_NAME_MESSAGES) != 0))
return 0;
#ifdef HAVE_RQUOTA
if (mount_type_is_nfs(root->mount)) {
T_BEGIN {
ret = root->user_disabled ? 0 :
do_rquota_user(root, &bytes_value, &bytes_limit, &count_value, &count_limit);
if (ret == 0 && !root->group_disabled)
ret = do_rquota_group(root, &bytes_value, &bytes_limit, &count_value, &count_limit);
} T_END;
} else
#endif
{
ret = fs_quota_get_resources(root, FALSE, &bytes_value, &bytes_limit,
&count_value, &count_limit);
if (ret == 0) {
/* fallback to group quota */
ret = fs_quota_get_resources(root, TRUE, &bytes_value, &bytes_limit,
&count_value, &count_limit);
}
}
if (ret <= 0)
return ret;
if (strcasecmp(name, QUOTA_NAME_STORAGE_BYTES) == 0)
*value_r = bytes_value;
else
*value_r = count_value;
if (_root->bytes_limit != (int64_t)bytes_limit ||
_root->count_limit != (int64_t)count_limit) {
/* update limit */
_root->bytes_limit = bytes_limit;
_root->count_limit = count_limit;
/* limits have changed, so we'll need to recalculate
relative quota rules */
quota_root_recalculate_relative_rules(_root->set, bytes_limit, count_limit);
}
return 1;
}
static int
fs_quota_update(struct quota_root *root ATTR_UNUSED,
struct quota_transaction_context *ctx ATTR_UNUSED)
{
return 0;
}
struct quota_backend quota_backend_fs = {
"fs",
{
fs_quota_alloc,
fs_quota_init,
fs_quota_deinit,
NULL,
NULL,
fs_quota_namespace_added,
fs_quota_root_get_resources,
fs_quota_get_resource,
fs_quota_update,
fs_quota_match_box,
NULL
}
};
#endif