quota-fs.c revision 4bbac9612c0b16bd4c7f03326a7a2cdc163014d8
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen/* Copyright (C) 2005-2006 Timo Sirainen */
a35cbba04d0a2823da98e693bd09a051addffdb2Timo Sirainen/* Only for reporting filesystem quota */
5e4c116a411967f1a012ac4f79732a724b5f6cc1Timo Sirainen# include <xfs/xqm.h> /* CentOS 4.x at least uses this */
4c74f086031dc6ce5c65cc0f949753a03a00c686Timo Sirainen/* Older sys/quota.h doesn't define _LINUX_QUOTA_VERSION at all, which means
4c74f086031dc6ce5c65cc0f949753a03a00c686Timo Sirainen it supports only v1 quota */
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenstatic void fs_quota_mountpoint_free(struct fs_quota_mountpoint *mount)
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenstatic void fs_quota_deinit(struct quota_root *_root)
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen struct fs_quota_root *root = (struct fs_quota_root *)_root;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenstatic struct fs_quota_mountpoint *fs_quota_mountpoint_get(const char *dir)
11ead0d848f1751c8274e3422fa48fc7d9aee2e9Timo Sirainen ret = mountpoint_get(dir, default_pool, &point);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenfs_quota_root_find_mountpoint(struct quota *quota,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen unsigned int i, count;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (i = 0; i < count; i++) {
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen if (roots[i]->backend.name == quota_backend_fs.name) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic void fs_quota_storage_added(struct quota *quota,
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen const char *dir;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen dir = mail_storage_get_mailbox_path(storage, "", &is_file);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_info("fs quota block device = %s", mount->device_path);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_info("fs quota mount point = %s", mount->mount_path);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen root = fs_quota_root_find_mountpoint(quota, mount);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* already exists */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* create a new root for this mountpoint */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen _root = quota_root_init(quota, quota_backend_fs.name);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* this is the default root. */
e4cbaae481f5e980dca4df1a819d8df127456e4dTimo Sirainen /* FIXME: pretty ugly to hardcode these */
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen mount->path = i_strconcat(mount->mount_path, "/quotas", NULL);
a35cbba04d0a2823da98e693bd09a051addffdb2Timo Sirainenstatic const char *const *
e4cbaae481f5e980dca4df1a819d8df127456e4dTimo Sirainenfs_quota_root_get_resources(struct quota_root *_root)
e4cbaae481f5e980dca4df1a819d8df127456e4dTimo Sirainen struct fs_quota_root *root = (struct fs_quota_root *)_root;
e4cbaae481f5e980dca4df1a819d8df127456e4dTimo Sirainen static const char *resources_kb[] = {
e4cbaae481f5e980dca4df1a819d8df127456e4dTimo Sirainen static const char *resources_kb_messages[] = {
e4cbaae481f5e980dca4df1a819d8df127456e4dTimo Sirainen return root->inode_per_mail ? resources_kb_messages : resources_kb;
9e96947aa6185341cbdd1140cb216fab3270ccbaTimo Sirainen/* retrieve user quota from a remote host */
e4cbaae481f5e980dca4df1a819d8df127456e4dTimo Sirainenstatic int do_rquota(struct fs_quota_root *root, bool bytes,
9e96947aa6185341cbdd1140cb216fab3270ccbaTimo Sirainen struct fs_quota_mountpoint *mount = root->mount;
9e96947aa6185341cbdd1140cb216fab3270ccbaTimo Sirainen i_error("quota-fs: %s is not a valid NFS device path",
9e96947aa6185341cbdd1140cb216fab3270ccbaTimo Sirainen host = t_strdup_until(mount->device_path, path);
9e96947aa6185341cbdd1140cb216fab3270ccbaTimo Sirainen /* clnt_create() polls for a while to establish a connection */
9e96947aa6185341cbdd1140cb216fab3270ccbaTimo Sirainen cl = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp");
9e96947aa6185341cbdd1140cb216fab3270ccbaTimo Sirainen i_error("quota-fs: could not contact RPC service on %s",
21aa3c764494fc308e8fdb1a9b4f9df3f260e4f8Timo Sirainen /* Establish some RPC credentials */
9e96947aa6185341cbdd1140cb216fab3270ccbaTimo Sirainen /* make the rquota call on the remote host */
9e96947aa6185341cbdd1140cb216fab3270ccbaTimo Sirainen timeout.tv_sec = RQUOTA_GETQUOTA_TIMEOUT_SECS;
9e96947aa6185341cbdd1140cb216fab3270ccbaTimo Sirainen call_status = clnt_call(cl, RQUOTAPROC_GETQUOTA,
9e96947aa6185341cbdd1140cb216fab3270ccbaTimo Sirainen (xdrproc_t)xdr_getquota_rslt, (char *)&result,
9e96947aa6185341cbdd1140cb216fab3270ccbaTimo Sirainen /* the result has been deserialized, let the client go */
9e96947aa6185341cbdd1140cb216fab3270ccbaTimo Sirainen const char *rpc_error_msg = clnt_sperrno(call_status);
9e96947aa6185341cbdd1140cb216fab3270ccbaTimo Sirainen i_error("quota-fs: remote rquota call failed: %s",
9e96947aa6185341cbdd1140cb216fab3270ccbaTimo Sirainen /* convert the results from blocks to bytes */
9e96947aa6185341cbdd1140cb216fab3270ccbaTimo Sirainen rquota *rq = &result.getquota_rslt_u.gqr_rquota;
9e96947aa6185341cbdd1140cb216fab3270ccbaTimo Sirainen (unsigned long long)*value_r,
9e96947aa6185341cbdd1140cb216fab3270ccbaTimo Sirainen i_error("quota-fs: permission denied to rquota service");
9e96947aa6185341cbdd1140cb216fab3270ccbaTimo Sirainen i_error("quota-fs: unrecognized status code (%d) "
4bbac9612c0b16bd4c7f03326a7a2cdc163014d8Timo Sirainen#if defined(FS_QUOTA_LINUX) || defined(FS_QUOTA_BSDAIX)
4bbac9612c0b16bd4c7f03326a7a2cdc163014d8Timo Sirainenstatic void fs_quota_root_disable(struct fs_quota_root *root, bool group)
4bbac9612c0b16bd4c7f03326a7a2cdc163014d8Timo Sirainenfs_quota_get_linux(struct fs_quota_root *root, bool group, bool bytes,
4bbac9612c0b16bd4c7f03326a7a2cdc163014d8Timo Sirainen i_error("%d quotactl(Q_XGETQUOTA, %s) failed: %m",
e4cbaae481f5e980dca4df1a819d8df127456e4dTimo Sirainen /* values always returned in 512 byte blocks */
a2871d937a75288bcd7177b2680d4e5096a7f3cbTimo Sirainen /* ext2, ext3 */
a2871d937a75288bcd7177b2680d4e5096a7f3cbTimo Sirainen i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
2e0e52f27469bf4eba8d7e88f1716f0a19824ba5Timo Sirainen i_error("Dovecot was compiled with Linux quota "
2e0e52f27469bf4eba8d7e88f1716f0a19824ba5Timo Sirainen "v%d support, try changing it "
2e0e52f27469bf4eba8d7e88f1716f0a19824ba5Timo Sirainen "(--with-linux-quota configure option)",
4bbac9612c0b16bd4c7f03326a7a2cdc163014d8Timo Sirainenfs_quota_get_bsdaix(struct fs_quota_root *root, bool group, bool bytes,
4bbac9612c0b16bd4c7f03326a7a2cdc163014d8Timo Sirainen if (quotactl(root->mount->mount_path, QCMD(Q_GETQUOTA, type),
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
e4cbaae481f5e980dca4df1a819d8df127456e4dTimo Sirainen *value_r = (uint64_t)dqblk.dqb_curblocks * DEV_BSIZE;
e4cbaae481f5e980dca4df1a819d8df127456e4dTimo Sirainen *limit_r = (uint64_t)dqblk.dqb_bsoftlimit * DEV_BSIZE;
29fe652c01b24298ee9e5825a103279106f0e263Timo Sirainenfs_quota_get_solaris(struct fs_quota_root *root, bool bytes,
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen if (ioctl(root->mount->fd, Q_QUOTACTL, &ctl) < 0) {
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen i_error("ioctl(%s, Q_QUOTACTL) failed: %m", root->mount->path);
e4cbaae481f5e980dca4df1a819d8df127456e4dTimo Sirainen *value_r = (uint64_t)dqblk.dqb_curblocks * DEV_BSIZE;
e4cbaae481f5e980dca4df1a819d8df127456e4dTimo Sirainen *limit_r = (uint64_t)dqblk.dqb_bsoftlimit * DEV_BSIZE;
4bbac9612c0b16bd4c7f03326a7a2cdc163014d8Timo Sirainenfs_quota_get_one_resource(struct fs_quota_root *root, bool group, bool bytes,
4bbac9612c0b16bd4c7f03326a7a2cdc163014d8Timo Sirainen return fs_quota_get_linux(root, group, bytes, value_r, limit_r);
4bbac9612c0b16bd4c7f03326a7a2cdc163014d8Timo Sirainen return fs_quota_get_bsdaix(root, group, bytes, value_r, limit_r);
4bbac9612c0b16bd4c7f03326a7a2cdc163014d8Timo Sirainen /* not supported */
4bbac9612c0b16bd4c7f03326a7a2cdc163014d8Timo Sirainen return fs_quota_get_solaris(root, bytes, value_r, limit_r);
29fe652c01b24298ee9e5825a103279106f0e263Timo Sirainenfs_quota_get_resource(struct quota_root *_root, const char *name,
29fe652c01b24298ee9e5825a103279106f0e263Timo Sirainen struct fs_quota_root *root = (struct fs_quota_root *)_root;
29fe652c01b24298ee9e5825a103279106f0e263Timo Sirainen (strcasecmp(name, QUOTA_NAME_STORAGE_BYTES) != 0 &&
29fe652c01b24298ee9e5825a103279106f0e263Timo Sirainen bytes = strcasecmp(name, QUOTA_NAME_STORAGE_BYTES) == 0;
29fe652c01b24298ee9e5825a103279106f0e263Timo Sirainen ret = do_rquota(root, bytes, value_r, limit_r);
4bbac9612c0b16bd4c7f03326a7a2cdc163014d8Timo Sirainen ret = fs_quota_get_one_resource(root, FALSE, bytes,
4bbac9612c0b16bd4c7f03326a7a2cdc163014d8Timo Sirainen /* fallback to group quota */
4bbac9612c0b16bd4c7f03326a7a2cdc163014d8Timo Sirainen return fs_quota_get_one_resource(root, TRUE, bytes, value_r, limit_r);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenfs_quota_update(struct quota_root *root __attr_unused__,