Update to 4.01.
[profile/ivi/quota.git] / quotaio_v1.c
1 /*
2  * Copyright (c) 1980, 1990 Regents of the University of California. All
3  * rights reserved.
4  * 
5  * This code is derived from software contributed to Berkeley by Robert Elz at
6  * The University of Melbourne.
7  * 
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met: 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer. 2.
12  * Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution. 3. All advertising
15  * materials mentioning features or use of this software must display the
16  * following acknowledgement: This product includes software developed by the
17  * University of California, Berkeley and its contributors. 4. Neither the
18  * name of the University nor the names of its contributors may be used to
19  * endorse or promote products derived from this software without specific
20  * prior written permission.
21  * 
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
23  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25  * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
26  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include "config.h"
36
37 #include <unistd.h>
38 #include <errno.h>
39 #include <string.h>
40 #include <stdlib.h>
41
42 #include "pot.h"
43 #include "common.h"
44 #include "quotaio_v1.h"
45 #include "dqblk_v1.h"
46 #include "quotaio.h"
47 #include "quotasys.h"
48 #include "quotaio_generic.h"
49
50 static int v1_check_file(int fd, int type, int fmt);
51 static int v1_init_io(struct quota_handle *h);
52 static int v1_new_io(struct quota_handle *h);
53 static int v1_write_info(struct quota_handle *h);
54 static struct dquot *v1_read_dquot(struct quota_handle *h, qid_t id);
55 static int v1_commit_dquot(struct dquot *dquot, int flags);
56 static int v1_scan_dquots(struct quota_handle *h, int (*process_dquot) (struct dquot *dquot, char *dqname));
57
58 struct quotafile_ops quotafile_ops_1 = {
59 check_file:     v1_check_file,
60 init_io:        v1_init_io,
61 new_io:         v1_new_io,
62 write_info:     v1_write_info,
63 read_dquot:     v1_read_dquot,
64 commit_dquot:   v1_commit_dquot,
65 scan_dquots:    v1_scan_dquots,
66 };
67
68 /*
69  *      Copy dquot from disk to memory
70  */
71 static inline void v1_disk2memdqblk(struct util_dqblk *m, struct v1_disk_dqblk *d)
72 {
73         m->dqb_ihardlimit = d->dqb_ihardlimit;
74         m->dqb_isoftlimit = d->dqb_isoftlimit;
75         m->dqb_bhardlimit = d->dqb_bhardlimit;
76         m->dqb_bsoftlimit = d->dqb_bsoftlimit;
77         m->dqb_curinodes = d->dqb_curinodes;
78         m->dqb_curspace = ((qsize_t)d->dqb_curblocks) * V1_DQBLK_SIZE;
79         m->dqb_itime = d->dqb_itime;
80         m->dqb_btime = d->dqb_btime;
81 }
82
83 /*
84  *      Copy dquot from memory to disk
85  */
86 static inline void v1_mem2diskdqblk(struct v1_disk_dqblk *d, struct util_dqblk *m)
87 {
88         d->dqb_ihardlimit = m->dqb_ihardlimit;
89         d->dqb_isoftlimit = m->dqb_isoftlimit;
90         d->dqb_bhardlimit = m->dqb_bhardlimit;
91         d->dqb_bsoftlimit = m->dqb_bsoftlimit;
92         d->dqb_curinodes = m->dqb_curinodes;
93         d->dqb_curblocks = m->dqb_curspace >> V1_DQBLK_SIZE_BITS;
94         d->dqb_itime = m->dqb_itime;
95         d->dqb_btime = m->dqb_btime;
96 }
97
98 /* Convert kernel quotablock format to utility one */
99 static inline void v1_kern2utildqblk(struct util_dqblk *u, struct v1_kern_dqblk *k)
100 {
101         u->dqb_ihardlimit = k->dqb_ihardlimit;
102         u->dqb_isoftlimit = k->dqb_isoftlimit;
103         u->dqb_bhardlimit = k->dqb_bhardlimit;
104         u->dqb_bsoftlimit = k->dqb_bsoftlimit;
105         u->dqb_curinodes = k->dqb_curinodes;
106         u->dqb_curspace = ((qsize_t)k->dqb_curblocks) << V1_DQBLK_SIZE_BITS;
107         u->dqb_itime = k->dqb_itime;
108         u->dqb_btime = k->dqb_btime;
109 }
110
111 /* Convert utility quotablock format to kernel one */
112 static inline void v1_util2kerndqblk(struct v1_kern_dqblk *k, struct util_dqblk *u)
113 {
114         k->dqb_ihardlimit = u->dqb_ihardlimit;
115         k->dqb_isoftlimit = u->dqb_isoftlimit;
116         k->dqb_bhardlimit = u->dqb_bhardlimit;
117         k->dqb_bsoftlimit = u->dqb_bsoftlimit;
118         k->dqb_curinodes = u->dqb_curinodes;
119         k->dqb_curblocks = (u->dqb_curspace + V1_DQBLK_SIZE - 1) >> V1_DQBLK_SIZE_BITS;
120         k->dqb_itime = u->dqb_itime;
121         k->dqb_btime = u->dqb_btime;
122 }
123
124 /*
125  *      Check whether quotafile is in our format
126  */
127 static int v1_check_file(int fd, int type, int fmt)
128 {
129         struct stat st;
130
131         if (fstat(fd, &st) < 0)
132                 return 0;
133         if (!st.st_size || st.st_size % sizeof(struct v1_disk_dqblk))
134                 return 0;
135         return 1;
136 }
137
138 /*
139  *      Open quotafile
140  */
141 static int v1_init_io(struct quota_handle *h)
142 {
143         if (QIO_ENABLED(h)) {
144                 if (kernel_iface == IFACE_GENERIC) {
145                         if (vfs_get_info(h) < 0)
146                                 return -1;
147                 }
148                 else {
149                         struct v1_kern_dqblk kdqblk;
150
151                         if (quotactl(QCMD(Q_V1_GETQUOTA, h->qh_type), h->qh_quotadev, 0, (void *)&kdqblk) < 0) {
152                                 if (errno == EPERM) {   /* We have no permission to get this information? */
153                                         h->qh_info.dqi_bgrace = h->qh_info.dqi_igrace = 0;      /* It hopefully won't be needed */
154                                 }
155                                 else
156                                         return -1;
157                         }
158                         else {
159                                 h->qh_info.dqi_bgrace = kdqblk.dqb_btime;
160                                 h->qh_info.dqi_igrace = kdqblk.dqb_itime;
161                         }
162                 }
163         }
164         else {
165                 struct v1_disk_dqblk ddqblk;
166
167                 lseek(h->qh_fd, 0, SEEK_SET);
168                 if (read(h->qh_fd, &ddqblk, sizeof(ddqblk)) != sizeof(ddqblk))
169                         return -1;
170                 h->qh_info.dqi_bgrace = ddqblk.dqb_btime;
171                 h->qh_info.dqi_igrace = ddqblk.dqb_itime;
172         }
173         if (!h->qh_info.dqi_bgrace)
174                 h->qh_info.dqi_bgrace = MAX_DQ_TIME;
175         if (!h->qh_info.dqi_igrace)
176                 h->qh_info.dqi_igrace = MAX_IQ_TIME;
177         h->qh_info.dqi_max_b_limit = ~(uint32_t)0;
178         h->qh_info.dqi_max_i_limit = ~(uint32_t)0;
179         h->qh_info.dqi_max_b_usage = ((uint64_t)(~(uint32_t)0)) << V1_DQBLK_SIZE_BITS;
180         h->qh_info.dqi_max_i_usage = ~(uint32_t)0;
181
182         return 0;
183 }
184
185 /*
186  *      Initialize new quotafile
187  */
188 static int v1_new_io(struct quota_handle *h)
189 {
190         struct v1_disk_dqblk ddqblk;
191
192         /* Write at least roots dquot with grace times */
193         memset(&ddqblk, 0, sizeof(ddqblk));
194         ddqblk.dqb_btime = MAX_DQ_TIME;
195         ddqblk.dqb_itime = MAX_IQ_TIME;
196         h->qh_info.dqi_bgrace = MAX_DQ_TIME;
197         h->qh_info.dqi_igrace = MAX_IQ_TIME;
198         h->qh_info.dqi_max_b_limit = ~(uint32_t)0;
199         h->qh_info.dqi_max_i_limit = ~(uint32_t)0;
200         h->qh_info.dqi_max_b_usage = ((uint64_t)(~(uint32_t)0)) << V1_DQBLK_SIZE_BITS;
201         h->qh_info.dqi_max_i_usage = ~(uint32_t)0;
202         lseek(h->qh_fd, 0, SEEK_SET);
203         if (write(h->qh_fd, &ddqblk, sizeof(ddqblk)) != sizeof(ddqblk))
204                 return -1;
205         return 0;
206 }
207
208 /*
209  *      Write information (grace times to file)
210  */
211 static int v1_write_info(struct quota_handle *h)
212 {
213         if (QIO_RO(h)) {
214                 errstr(_("Trying to write info to readonly quotafile on %s.\n"), h->qh_quotadev);
215                 errno = EPERM;
216                 return -1;
217         }
218         if (QIO_ENABLED(h)) {
219                 if (kernel_iface == IFACE_GENERIC) {
220                         if (vfs_set_info(h, IIF_BGRACE | IIF_IGRACE) < 0)
221                                 return -1;
222                 }
223                 else {
224                         struct v1_kern_dqblk kdqblk;
225
226                         if (quotactl(QCMD(Q_V1_GETQUOTA, h->qh_type), h->qh_quotadev, 0, (void *)&kdqblk) < 0)
227                                 return -1;
228                         kdqblk.dqb_btime = h->qh_info.dqi_bgrace;
229                         kdqblk.dqb_itime = h->qh_info.dqi_igrace;
230                         if (quotactl(QCMD(Q_V1_SETQUOTA, h->qh_type), h->qh_quotadev, 0, (void *)&kdqblk) < 0)
231                                 return -1;
232                 }
233         }
234         else {
235                 struct v1_disk_dqblk ddqblk;
236
237                 lseek(h->qh_fd, 0, SEEK_SET);
238                 if (read(h->qh_fd, &ddqblk, sizeof(ddqblk)) != sizeof(ddqblk))
239                         return -1;
240                 ddqblk.dqb_btime = h->qh_info.dqi_bgrace;
241                 ddqblk.dqb_itime = h->qh_info.dqi_igrace;
242                 lseek(h->qh_fd, 0, SEEK_SET);
243                 if (write(h->qh_fd, &ddqblk, sizeof(ddqblk)) != sizeof(ddqblk))
244                         return -1;
245         }
246         return 0;
247 }
248
249 /*
250  *      Read a dqblk struct from the quotafile.
251  *      User can use 'errno' to detect errstr.
252  */
253 static struct dquot *v1_read_dquot(struct quota_handle *h, qid_t id)
254 {
255         struct v1_disk_dqblk ddqblk;
256         struct dquot *dquot = get_empty_dquot();
257
258         dquot->dq_id = id;
259         dquot->dq_h = h;
260         if (QIO_ENABLED(h)) {   /* Does kernel use the file? */
261                 if (kernel_iface == IFACE_GENERIC) {
262                         if (vfs_get_dquot(dquot) < 0) {
263                                 free(dquot);
264                                 return NULL;
265                         }
266                 }
267                 else {
268                         struct v1_kern_dqblk kdqblk;
269
270                         if (quotactl(QCMD(Q_V1_GETQUOTA, h->qh_type), h->qh_quotadev, id, (void *)&kdqblk) < 0) {
271                                 free(dquot);
272                                 return NULL;
273                         }
274                         v1_kern2utildqblk(&dquot->dq_dqb, &kdqblk);
275                 }
276         }
277         else {
278                 lseek(h->qh_fd, (long)V1_DQOFF(id), SEEK_SET);
279                 switch (read(h->qh_fd, &ddqblk, sizeof(ddqblk))) {
280                         case 0: /* EOF */
281                                 /*
282                                  * Convert implicit 0 quota (EOF) into an
283                                  * explicit one (zero'ed dqblk)
284                                  */
285                                 memset(&dquot->dq_dqb, 0, sizeof(struct util_dqblk));
286                                 break;
287                         case sizeof(struct v1_disk_dqblk):      /* OK */
288                                 v1_disk2memdqblk(&dquot->dq_dqb, &ddqblk);
289                                 break;
290                         default:        /* ERROR */
291                                 free(dquot);
292                                 return NULL;
293                 }
294         }
295         return dquot;
296 }
297
298 /*
299  *      Write a dqblk struct to the quotafile.
300  *      User can process use 'errno' to detect errstr
301  */
302 static int v1_commit_dquot(struct dquot *dquot, int flags)
303 {
304         struct v1_disk_dqblk ddqblk;
305         struct quota_handle *h = dquot->dq_h;
306
307         if (QIO_RO(h)) {
308                 errstr(_("Trying to write quota to readonly quotafile on %s\n"), h->qh_quotadev);
309                 errno = EPERM;
310                 return -1;
311         }
312         if (QIO_ENABLED(h)) {   /* Kernel uses same file? */
313                 if (kernel_iface == IFACE_GENERIC) {
314                         if (vfs_set_dquot(dquot, flags) < 0)
315                                 return -1;
316                 }
317                 else {
318                         struct v1_kern_dqblk kdqblk;
319                         int cmd;
320
321                         if (flags == COMMIT_USAGE)
322                                 cmd = Q_V1_SETUSE;
323                         else if (flags == COMMIT_LIMITS)
324                                 cmd = Q_V1_SETQLIM;
325                         else if (flags & COMMIT_TIMES) {
326                                 errno = EINVAL;
327                                 return -1;
328                         }
329                         else
330                                 cmd = Q_V1_SETQUOTA;
331                         v1_util2kerndqblk(&kdqblk, &dquot->dq_dqb);
332                         if (quotactl(QCMD(cmd, h->qh_type), h->qh_quotadev, dquot->dq_id,
333                              (void *)&kdqblk) < 0)
334                                 return -1;
335                 }
336         }
337         else {
338                 if (check_dquot_range(dquot) < 0) {
339                         errno = ERANGE;
340                         return -1;
341                 }
342                 v1_mem2diskdqblk(&ddqblk, &dquot->dq_dqb);
343                 lseek(h->qh_fd, (long)V1_DQOFF(dquot->dq_id), SEEK_SET);
344                 if (write(h->qh_fd, &ddqblk, sizeof(ddqblk)) != sizeof(ddqblk))
345                         return -1;
346         }
347         return 0;
348 }
349
350 /*
351  *      Scan all dquots in file and call callback on each
352  */
353 #define SCANBUFSIZE 256
354
355 static int v1_scan_dquots(struct quota_handle *h, int (*process_dquot) (struct dquot *, char *))
356 {
357         int rd, scanbufpos = 0, scanbufsize = 0;
358         char scanbuf[sizeof(struct v1_disk_dqblk)*SCANBUFSIZE];
359         struct v1_disk_dqblk *ddqblk;
360         struct dquot *dquot = get_empty_dquot();
361         qid_t id = 0;
362
363         memset(dquot, 0, sizeof(*dquot));
364         dquot->dq_h = h;
365         lseek(h->qh_fd, 0, SEEK_SET);
366         for(id = 0; ; id++, scanbufpos++) {
367                 if (scanbufpos >= scanbufsize) {
368                         rd = read(h->qh_fd, scanbuf, sizeof(scanbuf));
369                         if (rd < 0 || rd % sizeof(struct v1_disk_dqblk))
370                                 goto out_err;
371                         if (!rd)
372                                 break;
373                         scanbufpos = 0;
374                         scanbufsize = rd / sizeof(struct v1_disk_dqblk);
375                 }
376                 ddqblk = ((struct v1_disk_dqblk *)scanbuf) + scanbufpos;
377                 if ((ddqblk->dqb_ihardlimit | ddqblk->dqb_isoftlimit |
378                      ddqblk->dqb_bhardlimit | ddqblk->dqb_bsoftlimit |
379                      ddqblk->dqb_curblocks | ddqblk->dqb_curinodes |
380                      ddqblk->dqb_itime | ddqblk->dqb_btime) == 0)
381                         continue;
382                 v1_disk2memdqblk(&dquot->dq_dqb, ddqblk);
383                 dquot->dq_id = id;
384                 if ((rd = process_dquot(dquot, NULL)) < 0) {
385                         free(dquot);
386                         return rd;
387                 }
388         }
389         if (!rd) {              /* EOF? */
390                 free(dquot);
391                 return 0;
392         }
393 out_err:
394         free(dquot);
395         return -1;              /* Some read errstr... */
396 }