quota-fs.c revision c7ec3797312157be2b01925c95aff9599d04ef42
76b43e4417bab52e913da39b5f5bc2a130d3f149Timo Sirainen/* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */
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 */
3aaf560f83bd578d951b5026b5b0ffb58f86dccbTimo Sirainenstatic int fs_quota_init(struct quota_root *_root, const char *args)
3aaf560f83bd578d951b5026b5b0ffb58f86dccbTimo Sirainen struct fs_quota_root *root = (struct fs_quota_root *)_root;
5670571cacaeb192fdc26ca2efd79734381796c0Timo Sirainen const char *const *tmp;
5670571cacaeb192fdc26ca2efd79734381796c0Timo Sirainen for (tmp = t_strsplit(args, ":"); *tmp != NULL; tmp++) {
5670571cacaeb192fdc26ca2efd79734381796c0Timo Sirainen root->storage_mount_path = i_strdup(*tmp + 6);
5670571cacaeb192fdc26ca2efd79734381796c0Timo Sirainen i_error("fs quota: Invalid parameter: %s", *tmp);
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);
5670571cacaeb192fdc26ca2efd79734381796c0Timo Sirainen ((root)->root.backend.name == quota_backend_fs.name && \
5670571cacaeb192fdc26ca2efd79734381796c0Timo Sirainen strcmp((root)->storage_mount_path, (mount)->mount_path) == 0))
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenfs_quota_root_find_mountpoint(struct quota *quota,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen unsigned int i, count;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (i = 0; i < count; i++) {
5670571cacaeb192fdc26ca2efd79734381796c0Timo Sirainen struct fs_quota_root *root = (struct fs_quota_root *)roots[i];
ceb88add1fc9f7f25e1ce75f7dae947bb63ae0a0Timo Sirainenstatic void fs_quota_mount_init(struct fs_quota_root *root,
3aaf560f83bd578d951b5026b5b0ffb58f86dccbTimo Sirainen unsigned int i, count;
3aaf560f83bd578d951b5026b5b0ffb58f86dccbTimo Sirainen mount->path = i_strconcat(mount->mount_path, "/quotas", NULL);
3aaf560f83bd578d951b5026b5b0ffb58f86dccbTimo Sirainen /* if there are more unused quota roots, copy this mount to them */
ceb88add1fc9f7f25e1ce75f7dae947bb63ae0a0Timo Sirainen roots = array_get(&root->root.quota->roots, &count);
3aaf560f83bd578d951b5026b5b0ffb58f86dccbTimo Sirainen for (i = 0; i < count; i++) {
5670571cacaeb192fdc26ca2efd79734381796c0Timo Sirainen if (QUOTA_ROOT_MATCH(root, mount) && root->mount == NULL) {
ceb88add1fc9f7f25e1ce75f7dae947bb63ae0a0Timo Sirainenstatic void fs_quota_add_missing_mounts(struct quota *quota)
ceb88add1fc9f7f25e1ce75f7dae947bb63ae0a0Timo Sirainen unsigned int i, count;
ceb88add1fc9f7f25e1ce75f7dae947bb63ae0a0Timo Sirainen for (i = 0; i < count; i++) {
ceb88add1fc9f7f25e1ce75f7dae947bb63ae0a0Timo Sirainen struct fs_quota_root *root = (struct fs_quota_root *)roots[i];
ceb88add1fc9f7f25e1ce75f7dae947bb63ae0a0Timo Sirainen if (root->root.backend.name != quota_backend_fs.name ||
ceb88add1fc9f7f25e1ce75f7dae947bb63ae0a0Timo Sirainen root->storage_mount_path == NULL || root->mount != NULL)
ceb88add1fc9f7f25e1ce75f7dae947bb63ae0a0Timo Sirainen mount = fs_quota_mountpoint_get(root->storage_mount_path);
ceb88add1fc9f7f25e1ce75f7dae947bb63ae0a0Timo Sirainenstatic void fs_quota_storage_added(struct quota *quota,
ceb88add1fc9f7f25e1ce75f7dae947bb63ae0a0Timo Sirainen const char *dir;
ceb88add1fc9f7f25e1ce75f7dae947bb63ae0a0Timo Sirainen dir = mail_storage_get_mailbox_path(storage, "", &is_file);
b19feb85569a6ad96e60d8246723561331213b8aTimo Sirainen i_info("fs quota block device = %s", mount->device_path);
b19feb85569a6ad96e60d8246723561331213b8aTimo Sirainen i_info("fs quota mount point = %s", mount->mount_path);
b19feb85569a6ad96e60d8246723561331213b8aTimo Sirainen root = fs_quota_root_find_mountpoint(quota, mount);
ceb88add1fc9f7f25e1ce75f7dae947bb63ae0a0Timo Sirainen /* we would actually want to do this only once after all quota roots
ceb88add1fc9f7f25e1ce75f7dae947bb63ae0a0Timo Sirainen are created, but there's no way to do this right now */
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;
45031ba153608eb33af22ee0d242c3e5e426486dTimo Sirainenfs_quota_get_hpux(struct fs_quota_root *root, bool bytes,
45031ba153608eb33af22ee0d242c3e5e426486dTimo Sirainen if (quotactl(Q_GETQUOTA, root->mount->device_path,
45031ba153608eb33af22ee0d242c3e5e426486dTimo Sirainen i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
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 */
45031ba153608eb33af22ee0d242c3e5e426486dTimo Sirainen return fs_quota_get_hpux(root, bytes, value_r, limit_r);
4bbac9612c0b16bd4c7f03326a7a2cdc163014d8Timo Sirainen return fs_quota_get_solaris(root, bytes, value_r, limit_r);
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainenstatic bool fs_quota_match_box(struct quota_root *_root, struct mailbox *box)
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen struct fs_quota_root *root = (struct fs_quota_root *)_root;
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen mailbox_path = mail_storage_get_mailbox_path(box->storage, box->name,
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen if (stat(root->storage_mount_path, &rst) < 0) {
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen i_info("box=%s mount=%s match=%s", mailbox_path,
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen root->storage_mount_path, match ? "yes" : "no");
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;
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen ret = do_rquota(root, bytes, value_r, &limit);
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen ret = fs_quota_get_one_resource(root, FALSE, bytes,
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen /* fallback to group quota */
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen ret = fs_quota_get_one_resource(root, TRUE, bytes,
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen /* update limit */
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainenfs_quota_update(struct quota_root *root ATTR_UNUSED,