1/*
2   Unix SMB/CIFS implementation.
3   System QUOTA function wrappers for QUOTACTL_4A
4   Copyright (C) Stefan (metze) Metzmacher	2003
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 3 of the License, or
9   (at your option) any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program.  If not, see <http://www.gnu.org/licenses/>.
18*/
19
20
21#include "includes.h"
22
23#undef DBGC_CLASS
24#define DBGC_CLASS DBGC_QUOTA
25
26#ifndef HAVE_SYS_QUOTAS
27#ifdef HAVE_QUOTACTL_4A
28#undef HAVE_QUOTACTL_4A
29#endif
30#endif
31
32#ifdef HAVE_QUOTACTL_4A
33/* long quotactl(int cmd, char *special, qid_t id, caddr_t addr) */
34/* this is used by: HPUX,IRIX */
35
36#ifdef HAVE_SYS_TYPES_H
37#include <sys/types.h>
38#endif
39
40#ifdef HAVE_ASM_TYPES_H
41#include <asm/types.h>
42#endif
43
44#ifdef HAVE_SYS_QUOTA_H
45#include <sys/quota.h>
46#endif
47
48#ifndef Q_SETQLIM
49#define Q_SETQLIM Q_SETQUOTA
50#endif
51
52#ifndef QCMD
53#define QCMD(x,y) x
54#endif
55
56#ifndef QCMD
57#define QCMD(x,y) x
58#endif
59
60#ifdef GRPQUOTA
61#define HAVE_GROUP_QUOTA
62#endif
63
64#ifndef QUOTABLOCK_SIZE
65#define QUOTABLOCK_SIZE DEV_BSIZE
66#endif
67
68#ifdef HAVE_DQB_FSOFTLIMIT
69#define dqb_isoftlimit	dqb_fsoftlimit
70#define dqb_ihardlimit	dqb_fhardlimit
71#define dqb_curinodes	dqb_curfiles
72#endif
73
74#ifdef INITQFNAMES
75#define USERQUOTAFILE_EXTENSION ".user"
76#else
77#define USERQUOTAFILE_EXTENSION ""
78#endif
79
80#if !defined(QUOTAFILENAME) && defined(QFILENAME)
81#define QUOTAFILENAME QFILENAME
82#endif
83
84/****************************************************************************
85 Abstract out the quotactl_4A get calls.
86****************************************************************************/
87int sys_get_vfs_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
88{
89	int ret = -1;
90	uint32 qflags = 0;
91	struct dqblk D;
92	uint64_t bsize = (uint64_t)QUOTABLOCK_SIZE;
93
94	ZERO_STRUCT(D);
95	ZERO_STRUCT(*dp);
96	dp->qtype = qtype;
97
98	switch (qtype) {
99		case SMB_USER_QUOTA_TYPE:
100			DEBUG(10,("sys_get_vfs_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
101				path, bdev, (unsigned)id.uid));
102
103			if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), (caddr_t)bdev, id.uid, (void *)&D))&&errno != EDQUOT) {
104				return ret;
105			}
106
107			if ((D.dqb_curblocks==0)&&
108				(D.dqb_bsoftlimit==0)&&
109				(D.dqb_bhardlimit==0)) {
110				/* the upper layer functions don't want empty quota records...*/
111				return -1;
112			}
113
114			break;
115#ifdef HAVE_GROUP_QUOTA
116		case SMB_GROUP_QUOTA_TYPE:
117			DEBUG(10,("sys_get_vfs_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
118				path, bdev, (unsigned)id.gid));
119
120			if ((ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), (caddr_t)bdev, id.gid, (void *)&D))&&errno != EDQUOT) {
121				return ret;
122			}
123
124			if ((D.dqb_curblocks==0)&&
125				(D.dqb_bsoftlimit==0)&&
126				(D.dqb_bhardlimit==0)) {
127				/* the upper layer functions don't want empty quota records...*/
128				return -1;
129			}
130
131			break;
132#endif /* HAVE_GROUP_QUOTA */
133		case SMB_USER_FS_QUOTA_TYPE:
134			id.uid = getuid();
135
136			DEBUG(10,("sys_get_vfs_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
137				path, (caddr_t)bdev, (unsigned)id.uid));
138
139			if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), (caddr_t)bdev, id.uid, (void *)&D))==0) {
140				qflags |= QUOTAS_DENY_DISK;
141			}
142
143			ret = 0;
144			break;
145#ifdef HAVE_GROUP_QUOTA
146		case SMB_GROUP_FS_QUOTA_TYPE:
147			id.gid = getgid();
148
149			DEBUG(10,("sys_get_vfs_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
150				path, bdev, (unsigned)id.gid));
151
152			if ((ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), (caddr_t)bdev, id.gid, (void *)&D))==0) {
153				qflags |= QUOTAS_DENY_DISK;
154			}
155
156			ret = 0;
157			break;
158#endif /* HAVE_GROUP_QUOTA */
159		default:
160			errno = ENOSYS;
161			return -1;
162	}
163
164	dp->bsize = bsize;
165	dp->softlimit = (uint64_t)D.dqb_bsoftlimit;
166	dp->hardlimit = (uint64_t)D.dqb_bhardlimit;
167	dp->ihardlimit = (uint64_t)D.dqb_ihardlimit;
168	dp->isoftlimit = (uint64_t)D.dqb_isoftlimit;
169	dp->curinodes = (uint64_t)D.dqb_curinodes;
170	dp->curblocks = (uint64_t)D.dqb_curblocks;
171
172
173	dp->qflags = qflags;
174
175	return ret;
176}
177
178/****************************************************************************
179 Abstract out the quotactl_4A set calls.
180****************************************************************************/
181int sys_set_vfs_quota(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
182{
183	int ret = -1;
184	uint32 qflags = 0;
185	uint32 oldqflags = 0;
186	struct dqblk D;
187	uint64_t bsize = (uint64_t)QUOTABLOCK_SIZE;
188
189	ZERO_STRUCT(D);
190
191	if (bsize == dp->bsize) {
192		D.dqb_bsoftlimit = dp->softlimit;
193		D.dqb_bhardlimit = dp->hardlimit;
194		D.dqb_ihardlimit = dp->ihardlimit;
195		D.dqb_isoftlimit = dp->isoftlimit;
196	} else {
197		D.dqb_bsoftlimit = (dp->softlimit*dp->bsize)/bsize;
198		D.dqb_bhardlimit = (dp->hardlimit*dp->bsize)/bsize;
199		D.dqb_ihardlimit = (dp->ihardlimit*dp->bsize)/bsize;
200		D.dqb_isoftlimit = (dp->isoftlimit*dp->bsize)/bsize;
201	}
202
203	qflags = dp->qflags;
204
205	switch (qtype) {
206		case SMB_USER_QUOTA_TYPE:
207			DEBUG(10,("sys_set_vfs_quota: path[%s] bdev[%s] SMB_USER_QUOTA_TYPE uid[%u]\n",
208				path, bdev, (unsigned)id.uid));
209
210			ret = quotactl(QCMD(Q_SETQLIM,USRQUOTA), (caddr_t)bdev, id.uid, (void *)&D);
211			break;
212#ifdef HAVE_GROUP_QUOTA
213		case SMB_GROUP_QUOTA_TYPE:
214			DEBUG(10,("sys_set_vfs_quota: path[%s] bdev[%s] SMB_GROUP_QUOTA_TYPE gid[%u]\n",
215				path, bdev, (unsigned)id.gid));
216
217			ret = quotactl(QCMD(Q_SETQLIM,GRPQUOTA), (caddr_t)bdev, id.gid, (void *)&D);
218			break;
219#endif /* HAVE_GROUP_QUOTA */
220		case SMB_USER_FS_QUOTA_TYPE:
221			/* this stuff didn't work as it should:
222			 * switching on/off quota via quotactl()
223			 * didn't work!
224			 * So we just return 0
225			 * --metze
226			 *
227			 * On HPUX we didn't have the mount path,
228			 * we need to fix sys_path_to_bdev()
229			 *
230			 */
231			id.uid = getuid();
232			DEBUG(10,("sys_set_vfs_quota: path[%s] bdev[%s] SMB_USER_FS_QUOTA_TYPE (uid[%u])\n",
233				path, bdev, (unsigned)id.uid));
234
235#if 0
236			ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), (caddr_t)bdev, id.uid, (void *)&D);
237
238			if ((qflags&QUOTAS_DENY_DISK)||(qflags&QUOTAS_ENABLED)) {
239				if (ret == 0) {
240					char *quota_file = NULL;
241
242					asprintf(&quota_file,"/%s/%s%s",path, QUOTAFILENAME,USERQUOTAFILE_EXTENSION);
243					if (quota_file == NULL) {
244						DEBUG(0,("asprintf() failed!\n"));
245						errno = ENOMEM;
246						return -1;
247					}
248
249					ret = quotactl(QCMD(Q_QUOTAON,USRQUOTA), (caddr_t)bdev, -1,(void *)quota_file);
250				} else {
251					ret = 0;
252				}
253			} else {
254				if (ret != 0) {
255					/* turn off */
256					ret = quotactl(QCMD(Q_QUOTAOFF,USRQUOTA), (caddr_t)bdev, -1, (void *)0);
257				} else {
258					ret = 0;
259				}
260			}
261
262			DEBUG(0,("sys_set_vfs_quota: ret(%d) errno(%d)[%s] uid(%d) bdev[%s]\n",
263				ret,errno,strerror(errno),id.uid,bdev));
264#else
265			if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), (caddr_t)bdev, id.uid, (void *)&D))==0) {
266				oldqflags |= QUOTAS_DENY_DISK;
267			}
268
269			if (oldqflags == qflags) {
270				ret = 0;
271			} else {
272				ret = -1;
273			}
274#endif
275			break;
276#ifdef HAVE_GROUP_QUOTA
277		case SMB_GROUP_FS_QUOTA_TYPE:
278			/* this stuff didn't work as it should:
279			 * switching on/off quota via quotactl()
280			 * didn't work!
281			 * So we just return 0
282			 * --metze
283			 *
284			 * On HPUX we didn't have the mount path,
285			 * we need to fix sys_path_to_bdev()
286			 *
287			 */
288			id.gid = getgid();
289			DEBUG(10,("sys_set_vfs_quota: path[%s] bdev[%s] SMB_GROUP_FS_QUOTA_TYPE (gid[%u])\n",
290				path, bdev, (unsigned)id.gid));
291
292#if 0
293			ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), bdev, id, (void *)&D);
294
295			if ((qflags&QUOTAS_DENY_DISK)||(qflags&QUOTAS_ENABLED)) {
296				if (ret == 0) {
297					char *quota_file = NULL;
298
299					asprintf(&quota_file,"/%s/%s%s",path, QUOTAFILENAME,GROUPQUOTAFILE_EXTENSION);
300					if (quota_file == NULL) {
301						DEBUG(0,("asprintf() failed!\n"));
302						errno = ENOMEM;
303						return -1;
304					}
305
306					ret = quotactl(QCMD(Q_QUOTAON,GRPQUOTA), (caddr_t)bdev, -1,(void *)quota_file);
307				} else {
308					ret = 0;
309				}
310			} else {
311				if (ret != 0) {
312					/* turn off */
313					ret = quotactl(QCMD(Q_QUOTAOFF,GRPQUOTA), (caddr_t)bdev, -1, (void *)0);
314				} else {
315					ret = 0;
316				}
317			}
318
319			DEBUG(0,("sys_set_vfs_quota: ret(%d) errno(%d)[%s] uid(%d) bdev[%s]\n",
320				ret,errno,strerror(errno),id.gid,bdev));
321#else
322			if ((ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), (caddr_t)bdev, id.gid, (void *)&D))==0) {
323				oldqflags |= QUOTAS_DENY_DISK;
324			}
325
326			if (oldqflags == qflags) {
327				ret = 0;
328			} else {
329				ret = -1;
330			}
331#endif
332			break;
333#endif /* HAVE_GROUP_QUOTA */
334		default:
335			errno = ENOSYS;
336			return -1;
337	}
338
339	return ret;
340}
341
342#else /* HAVE_QUOTACTL_4A */
343 void dummy_sysquotas_4A(void);
344
345 void dummy_sysquotas_4A(void){}
346#endif /* HAVE_QUOTACTL_4A */
347