quotafile.c revision 188598
1/*-
2 * Copyright (c) 2008 Dag-Erling Co��dan Sm��rgrav
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer
10 *    in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: projects/quota64/lib/libutil/quotafile.c 188598 2009-02-13 19:56:59Z mckusick $
28 */
29
30#include <sys/types.h>
31#include <sys/endian.h>
32#include <sys/mount.h>
33#include <sys/stat.h>
34
35#include <ufs/ufs/quota.h>
36
37#include <errno.h>
38#include <fcntl.h>
39#include <fstab.h>
40#include <grp.h>
41#include <pwd.h>
42#include <libutil.h>
43#include <stdint.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48
49struct quotafile {
50	int fd;
51	int type; /* 32 or 64 */
52};
53
54static const char *qfextension[] = INITQFNAMES;
55
56struct quotafile *
57quota_open(const char *fn)
58{
59	struct quotafile *qf;
60	struct dqhdr64 dqh;
61	int serrno;
62
63	if ((qf = calloc(1, sizeof(*qf))) == NULL)
64		return (NULL);
65	if ((qf->fd = open(fn, O_RDWR)) < 0) {
66		serrno = errno;
67		free(qf);
68		errno = serrno;
69		return (NULL);
70	}
71	qf->type = 32;
72	switch (read(qf->fd, &dqh, sizeof(dqh))) {
73	case -1:
74		serrno = errno;
75		close(qf->fd);
76		free(qf);
77		errno = serrno;
78		return (NULL);
79	case sizeof(dqh):
80		if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) != 0) {
81			/* no magic, assume 32 bits */
82			qf->type = 32;
83			return (qf);
84		}
85		if (be32toh(dqh.dqh_version) != Q_DQHDR64_VERSION ||
86		    be32toh(dqh.dqh_hdrlen) != sizeof(struct dqhdr64) ||
87		    be32toh(dqh.dqh_reclen) != sizeof(struct dqblk64)) {
88			/* correct magic, wrong version / lengths */
89			close(qf->fd);
90			free(qf);
91			errno = EINVAL;
92			return (NULL);
93		}
94		qf->type = 64;
95		return (qf);
96	default:
97		qf->type = 32;
98		return (qf);
99	}
100	/* not reached */
101}
102
103struct quotafile *
104quota_create(const char *fn)
105{
106	struct quotafile *qf;
107	struct dqhdr64 dqh;
108	struct group *grp;
109	int serrno;
110
111	if ((qf = calloc(1, sizeof(*qf))) == NULL)
112		return (NULL);
113	if ((qf->fd = open(fn, O_RDWR|O_CREAT|O_TRUNC, 0)) < 0) {
114		serrno = errno;
115		free(qf);
116		errno = serrno;
117		return (NULL);
118	}
119	qf->type = 64;
120	memset(&dqh, 0, sizeof(dqh));
121	memcpy(dqh.dqh_magic, Q_DQHDR64_MAGIC, sizeof(dqh.dqh_magic));
122	dqh.dqh_version = htobe32(Q_DQHDR64_VERSION);
123	dqh.dqh_hdrlen = htobe32(sizeof(struct dqhdr64));
124	dqh.dqh_reclen = htobe32(sizeof(struct dqblk64));
125	if (write(qf->fd, &dqh, sizeof(dqh)) != sizeof(dqh)) {
126		serrno = errno;
127		unlink(fn);
128		close(qf->fd);
129		free(qf);
130		errno = serrno;
131		return (NULL);
132	}
133	grp = getgrnam(QUOTAGROUP);
134	fchown(qf->fd, 0, grp ? grp->gr_gid : 0);
135	fchmod(qf->fd, 0640);
136	return (qf);
137}
138
139void
140quota_close(struct quotafile *qf)
141{
142
143	close(qf->fd);
144	free(qf);
145}
146
147static int
148quota_read32(struct quotafile *qf, struct dqblk *dqb, int id)
149{
150	struct dqblk32 dqb32;
151	off_t off;
152
153	off = id * sizeof(struct dqblk32);
154	if (lseek(qf->fd, off, SEEK_SET) == -1)
155		return (-1);
156	switch (read(qf->fd, &dqb32, sizeof(dqb32))) {
157	case 0:
158		memset(&dqb, 0, sizeof(*dqb));
159		return (0);
160	case sizeof(dqb32):
161		dqb->dqb_bhardlimit = dqb32.dqb_bhardlimit;
162		dqb->dqb_bsoftlimit = dqb32.dqb_bsoftlimit;
163		dqb->dqb_curblocks = dqb32.dqb_curblocks;
164		dqb->dqb_ihardlimit = dqb32.dqb_ihardlimit;
165		dqb->dqb_isoftlimit = dqb32.dqb_isoftlimit;
166		dqb->dqb_curinodes = dqb32.dqb_curinodes;
167		dqb->dqb_btime = dqb32.dqb_btime;
168		dqb->dqb_itime = dqb32.dqb_itime;
169		return (0);
170	default:
171		return (-1);
172	}
173}
174
175static int
176quota_read64(struct quotafile *qf, struct dqblk *dqb, int id)
177{
178	struct dqblk64 dqb64;
179	off_t off;
180
181	off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64);
182	if (lseek(qf->fd, off, SEEK_SET) == -1)
183		return (-1);
184	switch (read(qf->fd, &dqb64, sizeof(dqb64))) {
185	case 0:
186		memset(&dqb, 0, sizeof(*dqb));
187		return (0);
188	case sizeof(dqb64):
189		dqb->dqb_bhardlimit = be64toh(dqb64.dqb_bhardlimit);
190		dqb->dqb_bsoftlimit = be64toh(dqb64.dqb_bsoftlimit);
191		dqb->dqb_curblocks = be64toh(dqb64.dqb_curblocks);
192		dqb->dqb_ihardlimit = be64toh(dqb64.dqb_ihardlimit);
193		dqb->dqb_isoftlimit = be64toh(dqb64.dqb_isoftlimit);
194		dqb->dqb_curinodes = be64toh(dqb64.dqb_curinodes);
195		dqb->dqb_btime = be64toh(dqb64.dqb_btime);
196		dqb->dqb_itime = be64toh(dqb64.dqb_itime);
197		return (0);
198	default:
199		return (-1);
200	}
201}
202
203int
204quota_read(struct quotafile *qf, struct dqblk *dqb, int id)
205{
206
207	switch (qf->type) {
208	case 32:
209		return quota_read32(qf, dqb, id);
210	case 64:
211		return quota_read64(qf, dqb, id);
212	default:
213		errno = EINVAL;
214		return (-1);
215	}
216	/* not reached */
217}
218
219#define CLIP32(u64) ((u64) > UINT32_MAX ? UINT32_MAX : (uint32_t)(u64))
220
221static int
222quota_write32(struct quotafile *qf, const struct dqblk *dqb, int id)
223{
224	struct dqblk32 dqb32;
225	off_t off;
226
227	dqb32.dqb_bhardlimit = CLIP32(dqb->dqb_bhardlimit);
228	dqb32.dqb_bsoftlimit = CLIP32(dqb->dqb_bsoftlimit);
229	dqb32.dqb_curblocks = CLIP32(dqb->dqb_curblocks);
230	dqb32.dqb_ihardlimit = CLIP32(dqb->dqb_ihardlimit);
231	dqb32.dqb_isoftlimit = CLIP32(dqb->dqb_isoftlimit);
232	dqb32.dqb_curinodes = CLIP32(dqb->dqb_curinodes);
233	dqb32.dqb_btime = CLIP32(dqb->dqb_btime);
234	dqb32.dqb_itime = CLIP32(dqb->dqb_itime);
235
236	off = id * sizeof(struct dqblk32);
237	if (lseek(qf->fd, off, SEEK_SET) == -1)
238		return (-1);
239	return (write(qf->fd, &dqb32, sizeof(dqb32)) == sizeof(dqb32));
240}
241
242static int
243quota_write64(struct quotafile *qf, const struct dqblk *dqb, int id)
244{
245	struct dqblk64 dqb64;
246	off_t off;
247
248	dqb64.dqb_bhardlimit = htobe64(dqb->dqb_bhardlimit);
249	dqb64.dqb_bsoftlimit = htobe64(dqb->dqb_bsoftlimit);
250	dqb64.dqb_curblocks = htobe64(dqb->dqb_curblocks);
251	dqb64.dqb_ihardlimit = htobe64(dqb->dqb_ihardlimit);
252	dqb64.dqb_isoftlimit = htobe64(dqb->dqb_isoftlimit);
253	dqb64.dqb_curinodes = htobe64(dqb->dqb_curinodes);
254	dqb64.dqb_btime = htobe64(dqb->dqb_btime);
255	dqb64.dqb_itime = htobe64(dqb->dqb_itime);
256
257	off = sizeof(struct dqhdr64) + id * sizeof(struct dqblk64);
258	if (lseek(qf->fd, off, SEEK_SET) == -1)
259		return (-1);
260	return (write(qf->fd, &dqb64, sizeof(dqb64)) == sizeof(dqb64));
261}
262
263int
264quota_write(struct quotafile *qf, const struct dqblk *dqb, int id)
265{
266
267	switch (qf->type) {
268	case 32:
269		return quota_write32(qf, dqb, id);
270	case 64:
271		return quota_write64(qf, dqb, id);
272	default:
273		errno = EINVAL;
274		return (-1);
275	}
276	/* not reached */
277}
278
279/*
280 * Check to see if a particular quota is to be enabled.
281 */
282int
283hasquota(struct fstab *fs, int type, char *qfnamep, int qfbufsize)
284{
285	char *opt;
286	char *cp;
287	struct statfs sfb;
288	char buf[BUFSIZ];
289	static char initname, usrname[100], grpname[100];
290
291	if (!initname) {
292		(void)snprintf(usrname, sizeof(usrname), "%s%s",
293		    qfextension[USRQUOTA], QUOTAFILENAME);
294		(void)snprintf(grpname, sizeof(grpname), "%s%s",
295		    qfextension[GRPQUOTA], QUOTAFILENAME);
296		initname = 1;
297	}
298	strcpy(buf, fs->fs_mntops);
299	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
300		if ((cp = index(opt, '=')))
301			*cp++ = '\0';
302		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
303			break;
304		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
305			break;
306	}
307	if (!opt)
308		return (0);
309	/*
310	 * Ensure that the filesystem is mounted.
311	 */
312	if (statfs(fs->fs_file, &sfb) != 0 ||
313	    strcmp(fs->fs_file, sfb.f_mntonname)) {
314		return (0);
315	}
316	if (cp) {
317		strncpy(qfnamep, cp, qfbufsize);
318	} else {
319		(void)snprintf(qfnamep, qfbufsize, "%s/%s.%s", fs->fs_file,
320		    QUOTAFILENAME, qfextension[type]);
321	}
322	return (1);
323}
324