/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
/* Only for reporting filesystem quota */
#include "lib.h"
#include "array.h"
#include "str.h"
#include "hostpid.h"
#include "mountpoint.h"
#include "quota-private.h"
#include "quota-fs.h"
#ifdef HAVE_FS_QUOTA
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#ifdef HAVE_LINUX_DQBLK_XFS_H
# include <linux/dqblk_xfs.h>
# define HAVE_XFS_QUOTA
#elif defined (HAVE_XFS_XQM_H)
# define HAVE_XFS_QUOTA
#endif
#ifdef HAVE_RQUOTA
# include "rquota.h"
#endif
#ifndef DEV_BSIZE
# ifdef DQBSIZE
# else
# endif
#endif
#ifdef HAVE_STRUCT_DQBLK_CURSPACE
#endif
support for v1 entirely and again it doesn't define it. I guess we can just
assume v2 now, and if someone still wants v1 support they can add
-D_LINUX_QUOTA_VERSION=1 to CFLAGS. */
#ifndef _LINUX_QUOTA_VERSION
#endif
struct fs_quota_mountpoint {
int refcount;
char *mount_path;
char *device_path;
char *type;
unsigned int block_size;
#ifdef FS_QUOTA_SOLARIS
int fd;
char *path;
#endif
};
struct fs_quota_root {
char *storage_mount_path;
#ifdef FS_QUOTA_NETBSD
#endif
};
extern struct quota_backend quota_backend_fs;
{
}
{
}
{
}
{
}
{
}
const char **error_r)
{
{.param_name = NULL}
};
return -1;
return 0;
}
{
return;
#ifdef FS_QUOTA_SOLARIS
#endif
}
{
}
{
int ret;
if (ret <= 0)
return NULL;
#ifdef FS_QUOTA_SOLARIS
#endif
if (mount_type_is_nfs(mount)) {
i_error("quota-fs: %s is not a valid NFS device path",
mount->device_path);
return NULL;
}
}
return mount;
}
static struct fs_quota_root *
const struct fs_quota_mountpoint *mount)
{
unsigned int i, count;
for (i = 0; i < count; i++) {
mount->mount_path) == 0)
return root;
}
}
return empty;
}
static void
{
unsigned int i, count;
#ifdef FS_QUOTA_SOLARIS
#ifdef HAVE_RQUOTA
if (mount_type_is_nfs(mount)) {
/* using rquota for this mount */
} else
#endif
}
#endif
}
/* if there are more unused quota roots, copy this mount to them */
for (i = 0; i < count; i++) {
}
}
}
{
unsigned int i, count;
for (i = 0; i < count; i++) {
continue;
}
}
}
struct mail_namespace *ns)
{
const char *dir;
&dir))
else
else
}
/* we would actually want to do this only once after all quota roots
are created, but there's no way to do this right now */
}
static const char *const *
{
static const char *resources_kb[] = {
};
static const char *resources_kb_messages[] = {
};
}
#if defined(FS_QUOTA_LINUX) || defined(FS_QUOTA_BSDAIX) || \
defined(FS_QUOTA_NETBSD) || defined(HAVE_RQUOTA)
{
if (group)
else
}
#endif
#ifdef HAVE_RQUOTA
static void
{
/* use soft limits if they exist, fallback to hard limits */
/* convert the results from blocks to bytes */
if (rq->rq_bsoftlimit != 0) {
} else {
}
if (rq->rq_fsoftlimit != 0)
else
}
static int
const char **error_r)
{
const char *host;
char *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. */
while (*path == '/')
path++;
}
i_debug("quota-fs: host=%s, path=%s, uid=%s",
}
/* clnt_create() polls for a while to establish a connection */
"could not contact RPC service on %s", host);
return -1;
}
/* Establish some RPC credentials */
/* make the rquota call on the remote host */
timeout);
/* the result has been deserialized, let the client go */
if (call_status != RPC_SUCCESS) {
"remote rquota call failed: %s",
return -1;
}
case Q_OK: {
}
return 1;
}
case Q_NOQUOTA:
i_debug("quota-fs: uid=%s, limit=unlimited",
}
return 0;
case Q_EPERM:
*error_r = "permission denied to rquota service";
return -1;
default:
"unrecognized status code (%d) from rquota service",
return -1;
}
}
static int
const char **error_r)
{
#if defined(EXT_RQUOTAVERS) && defined(GRPQUOTA)
const char *host;
char *path;
path++;
i_debug("quota-fs: host=%s, path=%s, gid=%s",
}
/* clnt_create() polls for a while to establish a connection */
"could not contact RPC service on %s (group)", host);
return -1;
}
/* Establish some RPC credentials */
/* make the rquota call on the remote host */
timeout);
/* the result has been deserialized, let the client go */
if (call_status != RPC_SUCCESS) {
"remote ext rquota call failed: %s", rpc_error_msg);
return -1;
}
case Q_OK: {
}
return 1;
}
case Q_NOQUOTA:
i_debug("quota-fs: gid=%s, limit=unlimited",
}
return 0;
case Q_EPERM:
*error_r = "permission denied to ext rquota service";
return -1;
default:
"unrecognized status code (%d) from ext rquota service",
return -1;
}
#else
*error_r = "rquota not compiled with group support";
return -1;
#endif
}
#endif
#ifdef FS_QUOTA_LINUX
static int
const char **error_r)
{
#ifdef HAVE_XFS_QUOTA
return 0;
}
"errno=%d, quotactl(Q_XGETQUOTA, %s) failed: %m",
return -1;
}
/* values always returned in 512 byte blocks */
if (*bytes_limit_r == 0) {
}
if (*count_limit_r == 0) {
}
} else
#endif
{
/* ext2, ext3 */
return 0;
}
"quotactl(Q_GETQUOTA, %s) failed: %m",
"Dovecot was compiled with Linux quota "
"v%d support, try changing it "
"(CPPFLAGS=-D_LINUX_QUOTA_VERSION=%d configure)",
*error_r,
}
return -1;
}
#if _LINUX_QUOTA_VERSION == 1
#else
#endif
if (*bytes_limit_r == 0) {
}
if (*count_limit_r == 0) {
}
}
return 1;
}
#endif
#ifdef FS_QUOTA_BSDAIX
static int
const char **error_r)
{
return 0;
}
"quotactl(Q_GETQUOTA, %s) failed: %m",
return -1;
}
if (*bytes_limit_r == 0) {
}
if (*count_limit_r == 0) {
}
return 1;
}
#endif
#ifdef FS_QUOTA_NETBSD
static int
const char **error_r)
{
int ret;
return 0;
}
for (int i = 0; i < 2; i++) {
return 0;
}
"quotactl(Q_GETQUOTA, %s) failed: %m",
ret = -1;
break;
}
if (i == 0) {
} else {
}
ret = 1;
}
return ret;
}
#endif
#ifdef FS_QUOTA_HPUX
static int
const char **error_r)
{
return 0;
}
"quotactl(Q_GETQUOTA, %s) failed: %m",
return -1;
}
if (*bytes_limit_r == 0) {
}
if (*count_limit_r == 0) {
}
return 1;
}
#endif
#ifdef FS_QUOTA_SOLARIS
static int
const char **error_r)
{
return 0;
"ioctl(%s, Q_QUOTACTL) failed: %m",
return -1;
}
if (*bytes_limit_r == 0) {
}
if (*count_limit_r == 0) {
}
return 1;
}
#endif
static int
const char **error_r)
{
if (group) {
if (root->group_disabled)
return 0;
} else {
if (root->user_disabled)
return 0;
}
#ifdef FS_QUOTA_LINUX
#elif defined (FS_QUOTA_NETBSD)
#elif defined (FS_QUOTA_BSDAIX)
#else
if (group) {
/* not supported */
return 0;
}
#ifdef FS_QUOTA_HPUX
#else
#endif
#endif
}
{
const char *mailbox_path;
bool match;
return TRUE;
&mailbox_path) <= 0)
return FALSE;
return FALSE;
}
i_debug("stat(%s) failed: %m",
}
return FALSE;
}
}
return match;
}
static enum quota_get_result
{
int ret;
*value_r = 0;
"Mount point unknown for path %s",
else
*error_r = "Mount point unknown";
return QUOTA_GET_RESULT_INTERNAL_ERROR;
}
}
#ifdef HAVE_RQUOTA
&count_limit, error_r);
} else
#endif
{
&count_limit, error_r);
if (ret == 0) {
/* fallback to group quota */
&count_limit, error_r);
}
}
if (ret < 0)
return QUOTA_GET_RESULT_INTERNAL_ERROR;
else if (ret == 0)
return QUOTA_GET_RESULT_LIMITED;
*value_r = bytes_value;
else
*value_r = count_value;
/* update limit */
/* limits have changed, so we'll need to recalculate
relative quota rules */
}
return QUOTA_GET_RESULT_LIMITED;
}
static int
const char **error_r ATTR_UNUSED)
{
return 0;
}
.name = "fs",
.v = {
.alloc = fs_quota_alloc,
.init = fs_quota_init,
}
};
#endif