summaryrefslogtreecommitdiff
path: root/source3/lib/sysquotas_4B.c
blob: e3adc35015eba28bedc6f04a746703b4aa7e9fc3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/*
 * Unix SMB/CIFS implementation.
 * System QUOTA function wrappers for QUOTACTL_4B

 * Copyright (C) 2011 James Peach.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "includes.h"

#undef DBGC_CLASS
#define DBGC_CLASS DBGC_QUOTA

#ifndef HAVE_SYS_QUOTAS
#undef HAVE_QUOTACTL_4B
#endif

#ifdef HAVE_QUOTACTL_4B
/* int quotactl(const char *path, int cmd, int id, char *addr)
 *
 * This is used by many (all?) BSD-derived systems. This implementation has
 * been developed and tested on Darwin, but may also work on other BSD systems.
 */

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#ifdef HAVE_SYS_QUOTA_H
#include <sys/quota.h>
#endif

#ifdef HAVE_UFS_UFS_QUOTA_H
#include <ufs/ufs/quota.h>
#endif

#if defined(DARWINOS)
/* WorkARound broken HFS access checks in hfs_quotactl. Darwin only(?) */
#define HFS_QUOTACTL_WAR 1
#endif

static void xlate_qblk_to_smb(const struct dqblk * const qblk,
			SMB_DISK_QUOTA *dp)
{
	ZERO_STRUCTP(dp);

	DEBUG(10, ("unix softlimit=%u hardlimit=%u curblock=%u\n",
	    (unsigned)qblk->dqb_bsoftlimit, (unsigned)qblk->dqb_bhardlimit,
#ifdef HAVE_STRUCT_DQBLK_DQB_CURBYTES
	    (unsigned)qblk->dqb_curbytes));
#else
	    (unsigned)qblk->dqb_curblocks));
#endif

	DEBUGADD(10, ("unix softinodes=%u hardinodes=%u curinodes=%u\n",
	    (unsigned)qblk->dqb_isoftlimit, (unsigned)qblk->dqb_ihardlimit,
	    (unsigned)qblk->dqb_curinodes));

#ifdef HAVE_STRUCT_DQBLK_DQB_CURBYTES
	/* On Darwin, quotas are counted in bytes. We report them
	 * in 512b blocks because various callers have assumptions
	 * about the block size.
	 */
#define XLATE_TO_BLOCKS(bytes) (((bytes) + 1) / 512)
	dp->bsize = 512;

	dp->softlimit = XLATE_TO_BLOCKS(qblk->dqb_bsoftlimit);
	dp->hardlimit = XLATE_TO_BLOCKS(qblk->dqb_bhardlimit);
	dp->curblocks = XLATE_TO_BLOCKS(qblk->dqb_curbytes);
#undef XLATE_TO_BLOCKS
#endif

	dp->ihardlimit = qblk->dqb_ihardlimit;
	dp->isoftlimit = qblk->dqb_isoftlimit;
	dp->curinodes = qblk->dqb_curinodes;

	dp->qflags = QUOTAS_ENABLED | QUOTAS_DENY_DISK;

	DEBUG(10, ("softlimit=%u hardlimit=%u curblock=%u\n",
	    (unsigned)dp->softlimit, (unsigned)dp->hardlimit,
	    (unsigned)dp->curblocks));

	DEBUGADD(10, ("softinodes=%u hardinodes=%u curinodes=%u\n",
	    (unsigned)dp->isoftlimit, (unsigned)dp->ihardlimit,
	    (unsigned)dp->curinodes));

}

static void xlate_smb_to_qblk(const SMB_DISK_QUOTA * const dp,
			struct dqblk *qblk)
{
	ZERO_STRUCTP(qblk);

	qblk->dqb_bsoftlimit = dp->softlimit;
	qblk->dqb_bhardlimit = dp->hardlimit;
#ifdef HAVE_STRUCT_DQBLK_DQB_CURBYTES
	/* On Darwin, quotas are counted in bytes. */
	qblk->dqb_bsoftlimit *= dp->bsize;
	qblk->dqb_bhardlimit *= dp->bsize;
#endif
	qblk->dqb_ihardlimit = dp->ihardlimit;
	qblk->dqb_isoftlimit = dp->isoftlimit;
}

static int sys_quotactl_4B(const char * path, int cmd,
		int id, struct dqblk *qblk)
{
	int ret;

	/* NB: We must test GRPQUOTA here, because USRQUOTA is 0. */
	DEBUG(10, ("%s quota for %s ID %u on %s\n",
		    (cmd & QCMD(Q_GETQUOTA, 0)) ? "getting" : "setting",
		    (cmd & QCMD(0, GRPQUOTA)) ? "group" : "user",
		    (unsigned)id, path));

#ifdef HFS_QUOTACTL_WAR
	become_root();
#endif  /* HFS_QUOTACTL_WAR */

	ret = quotactl(path, cmd, id, qblk);
	if (ret == -1) {
		/* ENOTSUP means quota support is not compiled in. EINVAL
		 * means that quotas are not configured (commonly).
		 */
		if (errno != ENOTSUP && errno != EINVAL) {
			DEBUG(0, ("failed to %s quota for %s ID %u on %s: %s\n",
				    (cmd & QCMD(Q_GETQUOTA, 0)) ? "get" : "set",
				    (cmd & QCMD(0, GRPQUOTA)) ? "group" : "user",
				    (unsigned)id, path, strerror(errno)));
		}

#ifdef HFS_QUOTACTL_WAR
		unbecome_root();
#endif  /* HFS_QUOTACTL_WAR */


		return -1;
	}

#ifdef HFS_QUOTACTL_WAR
		unbecome_root();
#endif  /* HFS_QUOTACTL_WAR */

	return 0;
}

int sys_get_vfs_quota(const char *path, const char *bdev,
	enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
{
	int ret;
	struct dqblk qblk;

	ZERO_STRUCT(qblk);

	switch (qtype) {
	case SMB_USER_QUOTA_TYPE:
		/* Get quota for provided UID. */
		ret = sys_quotactl_4B(path, QCMD(Q_GETQUOTA, USRQUOTA),
					id.uid, &qblk);
		break;
	case SMB_USER_FS_QUOTA_TYPE:
		/* Get quota for current UID. */
		ret = sys_quotactl_4B(path, QCMD(Q_GETQUOTA, USRQUOTA),
					geteuid(), &qblk);
		break;
	case SMB_GROUP_QUOTA_TYPE:
		/* Get quota for provided GID. */
		ret = sys_quotactl_4B(path, QCMD(Q_GETQUOTA, GRPQUOTA),
					id.gid, &qblk);
		break;
	case SMB_GROUP_FS_QUOTA_TYPE:
		/* Get quota for current GID. */
		ret = sys_quotactl_4B(path, QCMD(Q_GETQUOTA, GRPQUOTA),
					getegid(), &qblk);
		break;
	default:
		DEBUG(0, ("cannot get unsupported quota type: %u\n",
			    (unsigned)qtype));
		errno = ENOSYS;
		return -1;
	}

	if (ret == -1) {
		return -1;
	}

	xlate_qblk_to_smb(&qblk, dp);
	dp->qtype = qtype;

	return ret;
}

int sys_set_vfs_quota(const char *path, const char *bdev,
	enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
{
	struct dqblk qblk;

	xlate_smb_to_qblk(dp, &qblk);

	switch (qtype) {
	case SMB_USER_QUOTA_TYPE:
		/* Set quota for provided UID. */
		return sys_quotactl_4B(path, QCMD(Q_SETQUOTA, USRQUOTA),
					id.uid, &qblk);
	case SMB_USER_FS_QUOTA_TYPE:
		/* Set quota for current UID. */
		return sys_quotactl_4B(path, QCMD(Q_SETQUOTA, USRQUOTA),
					geteuid(), &qblk);
	case SMB_GROUP_QUOTA_TYPE:
		/* Set quota for provided GID. */
		return sys_quotactl_4B(path, QCMD(Q_SETQUOTA, GRPQUOTA),
					id.gid, &qblk);
	case SMB_GROUP_FS_QUOTA_TYPE:
		/* Set quota for current GID. */
		return sys_quotactl_4B(path, QCMD(Q_SETQUOTA, GRPQUOTA),
					getegid(), &qblk);
	default:
		DEBUG(0, ("cannot set unsupported quota type: %u\n",
			    (unsigned)qtype));
		errno = ENOSYS;
		return -1;
	}
}

#endif /* HAVE_QUOTACTL_4B */