Initial commit to Gerrit
[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
178         return 0;
179 }
180
181 /*
182  *      Initialize new quotafile
183  */
184 static int v1_new_io(struct quota_handle *h)
185 {
186         struct v1_disk_dqblk ddqblk;
187
188         /* Write at least roots dquot with grace times */
189         memset(&ddqblk, 0, sizeof(ddqblk));
190         ddqblk.dqb_btime = MAX_DQ_TIME;
191         ddqblk.dqb_itime = MAX_IQ_TIME;
192         h->qh_info.dqi_bgrace = MAX_DQ_TIME;
193         h->qh_info.dqi_igrace = MAX_IQ_TIME;
194         lseek(h->qh_fd, 0, SEEK_SET);
195         if (write(h->qh_fd, &ddqblk, sizeof(ddqblk)) != sizeof(ddqblk))
196                 return -1;
197         return 0;
198 }
199
200 /*
201  *      Write information (grace times to file)
202  */
203 static int v1_write_info(struct quota_handle *h)
204 {
205         if (QIO_RO(h)) {
206                 errstr(_("Trying to write info to readonly quotafile on %s.\n"), h->qh_quotadev);
207                 errno = EPERM;
208                 return -1;
209         }
210         if (QIO_ENABLED(h)) {
211                 if (kernel_iface == IFACE_GENERIC) {
212                         if (vfs_set_info(h, IIF_BGRACE | IIF_IGRACE) < 0)
213                                 return -1;
214                 }
215                 else {
216                         struct v1_kern_dqblk kdqblk;
217
218                         if (quotactl(QCMD(Q_V1_GETQUOTA, h->qh_type), h->qh_quotadev, 0, (void *)&kdqblk) < 0)
219                                 return -1;
220                         kdqblk.dqb_btime = h->qh_info.dqi_bgrace;
221                         kdqblk.dqb_itime = h->qh_info.dqi_igrace;
222                         if (quotactl(QCMD(Q_V1_SETQUOTA, h->qh_type), h->qh_quotadev, 0, (void *)&kdqblk) < 0)
223                                 return -1;
224                 }
225         }
226         else {
227                 struct v1_disk_dqblk ddqblk;
228
229                 lseek(h->qh_fd, 0, SEEK_SET);
230                 if (read(h->qh_fd, &ddqblk, sizeof(ddqblk)) != sizeof(ddqblk))
231                         return -1;
232                 ddqblk.dqb_btime = h->qh_info.dqi_bgrace;
233                 ddqblk.dqb_itime = h->qh_info.dqi_igrace;
234                 lseek(h->qh_fd, 0, SEEK_SET);
235                 if (write(h->qh_fd, &ddqblk, sizeof(ddqblk)) != sizeof(ddqblk))
236                         return -1;
237         }
238         return 0;
239 }
240
241 /*
242  *      Read a dqblk struct from the quotafile.
243  *      User can use 'errno' to detect errstr.
244  */
245 static struct dquot *v1_read_dquot(struct quota_handle *h, qid_t id)
246 {
247         struct v1_disk_dqblk ddqblk;
248         struct dquot *dquot = get_empty_dquot();
249
250         dquot->dq_id = id;
251         dquot->dq_h = h;
252         if (QIO_ENABLED(h)) {   /* Does kernel use the file? */
253                 if (kernel_iface == IFACE_GENERIC) {
254                         if (vfs_get_dquot(dquot) < 0) {
255                                 free(dquot);
256                                 return NULL;
257                         }
258                 }
259                 else {
260                         struct v1_kern_dqblk kdqblk;
261
262                         if (quotactl(QCMD(Q_V1_GETQUOTA, h->qh_type), h->qh_quotadev, id, (void *)&kdqblk) < 0) {
263                                 free(dquot);
264                                 return NULL;
265                         }
266                         v1_kern2utildqblk(&dquot->dq_dqb, &kdqblk);
267                 }
268         }
269         else {
270                 lseek(h->qh_fd, (long)V1_DQOFF(id), SEEK_SET);
271                 switch (read(h->qh_fd, &ddqblk, sizeof(ddqblk))) {
272                         case 0: /* EOF */
273                                 /*
274                                  * Convert implicit 0 quota (EOF) into an
275                                  * explicit one (zero'ed dqblk)
276                                  */
277                                 memset(&dquot->dq_dqb, 0, sizeof(struct util_dqblk));
278                                 break;
279                         case sizeof(struct v1_disk_dqblk):      /* OK */
280                                 v1_disk2memdqblk(&dquot->dq_dqb, &ddqblk);
281                                 break;
282                         default:        /* ERROR */
283                                 free(dquot);
284                                 return NULL;
285                 }
286         }
287         return dquot;
288 }
289
290 /*
291  *      Write a dqblk struct to the quotafile.
292  *      User can process use 'errno' to detect errstr
293  */
294 static int v1_commit_dquot(struct dquot *dquot, int flags)
295 {
296         struct v1_disk_dqblk ddqblk;
297         struct quota_handle *h = dquot->dq_h;
298
299         if (QIO_RO(h)) {
300                 errstr(_("Trying to write quota to readonly quotafile on %s\n"), h->qh_quotadev);
301                 errno = EPERM;
302                 return -1;
303         }
304         if (QIO_ENABLED(h)) {   /* Kernel uses same file? */
305                 if (kernel_iface == IFACE_GENERIC) {
306                         if (vfs_set_dquot(dquot, flags) < 0)
307                                 return -1;
308                 }
309                 else {
310                         struct v1_kern_dqblk kdqblk;
311                         int cmd;
312
313                         if (flags == COMMIT_USAGE)
314                                 cmd = Q_V1_SETUSE;
315                         else if (flags == COMMIT_LIMITS)
316                                 cmd = Q_V1_SETQLIM;
317                         else if (flags & COMMIT_TIMES) {
318                                 errno = EINVAL;
319                                 return -1;
320                         }
321                         else
322                                 cmd = Q_V1_SETQUOTA;
323                         v1_util2kerndqblk(&kdqblk, &dquot->dq_dqb);
324                         if (quotactl(QCMD(cmd, h->qh_type), h->qh_quotadev, dquot->dq_id,
325                              (void *)&kdqblk) < 0)
326                                 return -1;
327                 }
328         }
329         else {
330                 v1_mem2diskdqblk(&ddqblk, &dquot->dq_dqb);
331                 lseek(h->qh_fd, (long)V1_DQOFF(dquot->dq_id), SEEK_SET);
332                 if (write(h->qh_fd, &ddqblk, sizeof(ddqblk)) != sizeof(ddqblk))
333                         return -1;
334         }
335         return 0;
336 }
337
338 /*
339  *      Scan all dquots in file and call callback on each
340  */
341 #define SCANBUFSIZE 256
342
343 static int v1_scan_dquots(struct quota_handle *h, int (*process_dquot) (struct dquot *, char *))
344 {
345         int rd, scanbufpos = 0, scanbufsize = 0;
346         char scanbuf[sizeof(struct v1_disk_dqblk)*SCANBUFSIZE];
347         struct v1_disk_dqblk *ddqblk;
348         struct dquot *dquot = get_empty_dquot();
349         qid_t id = 0;
350
351         if (QIO_ENABLED(h))     /* Kernel uses same file? */
352                 if (quotactl(QCMD((kernel_iface == IFACE_GENERIC) ? Q_SYNC : Q_6_5_SYNC, h->qh_type),
353                              h->qh_quotadev, 0, NULL) < 0)
354                         die(4, _("Cannot sync quotas on device %s: %s\n"), h->qh_quotadev,
355                             strerror(errno));
356         memset(dquot, 0, sizeof(*dquot));
357         dquot->dq_h = h;
358         lseek(h->qh_fd, 0, SEEK_SET);
359         for(id = 0; ; id++, scanbufpos++) {
360                 if (scanbufpos >= scanbufsize) {
361                         rd = read(h->qh_fd, scanbuf, sizeof(scanbuf));
362                         if (rd < 0 || rd % sizeof(struct v1_disk_dqblk))
363                                 goto out_err;
364                         if (!rd)
365                                 break;
366                         scanbufpos = 0;
367                         scanbufsize = rd / sizeof(struct v1_disk_dqblk);
368                 }
369                 ddqblk = ((struct v1_disk_dqblk *)scanbuf) + scanbufpos;
370                 if ((ddqblk->dqb_ihardlimit | ddqblk->dqb_isoftlimit |
371                      ddqblk->dqb_bhardlimit | ddqblk->dqb_bsoftlimit |
372                      ddqblk->dqb_curblocks | ddqblk->dqb_curinodes |
373                      ddqblk->dqb_itime | ddqblk->dqb_btime) == 0)
374                         continue;
375                 v1_disk2memdqblk(&dquot->dq_dqb, ddqblk);
376                 dquot->dq_id = id;
377                 if ((rd = process_dquot(dquot, NULL)) < 0) {
378                         free(dquot);
379                         return rd;
380                 }
381         }
382         if (!rd) {              /* EOF? */
383                 free(dquot);
384                 return 0;
385         }
386 out_err:
387         free(dquot);
388         return -1;              /* Some read errstr... */
389 }