quota-fs.c revision bcecb676b4f8ee1c8e16c47a711fee86859d84bf
/* Copyright (c) 2005-2015 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"
# define RQUOTA_GETQUOTA_TIMEOUT_SECS 10
#endif
#ifndef DEV_BSIZE
# ifdef DQBSIZE
# else
# define DEV_BSIZE 512
# endif
#endif
#ifdef HAVE_STRUCT_DQBLK_CURSPACE
# define dqb_curblocks dqb_curspace
#endif
it supports only v1 quota */
#ifndef _LINUX_QUOTA_VERSION
# define _LINUX_QUOTA_VERSION 1
#endif
#define mount_type_is_nfs(mount) \
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 {
struct quota_root root;
char *storage_mount_path;
struct fs_quota_mountpoint *mount;
unsigned int inode_per_mail:1;
unsigned int user_disabled:1;
unsigned int group_disabled:1;
#ifdef FS_QUOTA_NETBSD
struct quotahandle *qh;
#endif
};
extern struct quota_backend quota_backend_fs;
static struct quota_root *fs_quota_alloc(void)
{
struct fs_quota_root *root;
}
const char **error_r)
{
const char *const *tmp;
return 0;
} else {
return -1;
}
}
return 0;
}
{
return;
#ifdef FS_QUOTA_SOLARIS
}
#endif
}
{
}
{
struct fs_quota_mountpoint *mount;
struct mountpoint point;
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)
{
struct quota_root *const *roots;
unsigned int i, count;
for (i = 0; i < count; i++) {
mount->mount_path) == 0)
return root;
}
}
return empty;
}
static void
{
struct quota_root *const *roots;
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++) {
}
}
}
{
struct fs_quota_mountpoint *mount;
struct quota_root *const *roots;
unsigned int i, count;
for (i = 0; i < count; i++) {
continue;
}
}
}
struct mail_namespace *ns)
{
struct fs_quota_mountpoint *mount;
struct fs_quota_root *root;
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[] = {
};
}
#ifdef HAVE_RQUOTA
static void
{
/* use soft limits if they exist, fallback to hard limits */
if (bytes) {
/* convert the results from blocks to bytes */
if (rq->rq_bsoftlimit != 0) {
} else {
}
} else {
if (rq->rq_fsoftlimit != 0)
else
}
}
{
struct getquota_rslt result;
struct getquota_args args;
enum clnt_stat call_status;
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, %s",
}
/* clnt_create() polls for a while to establish a connection */
i_error("quota-fs: 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) {
i_error("quota-fs: remote rquota call failed: %s",
return -1;
}
case Q_OK: {
i_debug("quota-fs: uid=%s, value=%llu, limit=%llu",
(unsigned long long)*value_r,
(unsigned long long)*limit_r);
}
return 1;
}
case Q_NOQUOTA:
i_debug("quota-fs: uid=%s, limit=unlimited",
}
return 1;
case Q_EPERM:
i_error("quota-fs: permission denied to rquota service");
return -1;
default:
i_error("quota-fs: unrecognized status code (%d) "
return -1;
}
}
static int
{
#if defined(EXT_RQUOTAVERS) && defined(GRPQUOTA)
struct getquota_rslt result;
enum clnt_stat call_status;
const char *host;
char *path;
path++;
i_debug("quota-fs: host=%s, path=%s, gid=%s, %s",
}
/* clnt_create() polls for a while to establish a connection */
i_error("quota-fs: 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) {
i_error("quota-fs: remote ext rquota call failed: %s",
return -1;
}
case Q_OK: {
i_debug("quota-fs: gid=%s, value=%llu, limit=%llu",
(unsigned long long)*value_r,
(unsigned long long)*limit_r);
}
return 1;
}
case Q_NOQUOTA:
i_debug("quota-fs: gid=%s, limit=unlimited",
}
return 1;
case Q_EPERM:
i_error("quota-fs: permission denied to ext rquota service");
return -1;
default:
i_error("quota-fs: unrecognized status code (%d) "
return -1;
}
#else
i_error("quota-fs: rquota not compiled with group support");
return -1;
#endif
}
#endif
#if defined(FS_QUOTA_LINUX) || defined(FS_QUOTA_BSDAIX) || \
defined(FS_QUOTA_NETBSD)
{
if (group)
else
}
#endif
#ifdef FS_QUOTA_LINUX
static int
{
#ifdef HAVE_XFS_QUOTA
struct fs_disk_quota xdqblk;
return 0;
}
i_error("%d quotactl(Q_XGETQUOTA, %s) failed: %m",
return -1;
}
if (bytes) {
/* values always returned in 512 byte blocks */
if (*limit_r == 0) {
}
} else {
if (*limit_r == 0) {
}
}
} else
#endif
{
/* ext2, ext3 */
return 0;
}
i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
i_error("Dovecot was compiled with Linux quota "
"v%d support, try changing it "
"(CPPFLAGS=-D_LINUX_QUOTA_VERSION=%d configure)",
}
return -1;
}
if (bytes) {
#if _LINUX_QUOTA_VERSION == 1
#else
#endif
if (*limit_r == 0) {
}
} else {
if (*limit_r == 0) {
}
}
}
return 1;
}
#endif
#ifdef FS_QUOTA_BSDAIX
static int
{
return 0;
}
i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
return -1;
}
if (bytes) {
if (*limit_r == 0) {
}
} else {
if (*limit_r == 0) {
}
}
return 1;
}
#endif
#ifdef FS_QUOTA_NETBSD
static int
{
struct quotahandle *qh;
int ret;
i_error("cannot open quota for %s: %m",
return 0;
}
return 0;
}
i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
ret = -1;
} else {
if (bytes) {
} else {
}
ret = 1;
}
return ret;
}
#endif
#ifdef FS_QUOTA_HPUX
static int
{
return 0;
}
i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
return -1;
}
if (bytes) {
if (*limit_r == 0) {
}
} else {
if (*limit_r == 0) {
}
}
return 1;
}
#endif
#ifdef FS_QUOTA_SOLARIS
static int
{
return 0;
return -1;
}
if (bytes) {
if (*limit_r == 0) {
}
} else {
if (*limit_r == 0) {
}
}
return 1;
}
#endif
static int
{
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 int
{
bool bytes;
int ret;
*value_r = 0;
return 0;
#ifdef HAVE_RQUOTA
T_BEGIN {
} T_END;
} else
#endif
{
if (ret == 0) {
/* fallback to group quota */
}
}
if (ret <= 0)
return ret;
/* update limit */
if (bytes)
else
return 1;
}
static int
{
return 0;
}
struct quota_backend quota_backend_fs = {
"fs",
{
NULL,
NULL,
}
};
#endif