Initial commit to Gerrit
[profile/ivi/quota.git] / quotaops.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 <rpc/rpc.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <sys/file.h>
41 #include <sys/wait.h>
42 #include <errno.h>
43 #include <pwd.h>
44 #include <grp.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <signal.h>
48 #include <paths.h>
49 #include <unistd.h>
50 #include <time.h>
51 #include <ctype.h>
52
53 #if defined(RPC)
54 #include "rquota.h"
55 #endif
56
57 #include "mntopt.h"
58 #include "quotaops.h"
59 #include "pot.h"
60 #include "bylabel.h"
61 #include "common.h"
62 #include "quotasys.h"
63 #include "quotaio.h"
64
65 /*
66  * Set grace time if needed
67  */
68 void update_grace_times(struct dquot *q)
69 {
70         time_t now;
71
72         time(&now);
73         if (q->dq_dqb.dqb_bsoftlimit && toqb(q->dq_dqb.dqb_curspace) > q->dq_dqb.dqb_bsoftlimit) {
74                 if (!q->dq_dqb.dqb_btime)
75                         q->dq_dqb.dqb_btime = now + q->dq_h->qh_info.dqi_bgrace;
76         }
77         else
78                 q->dq_dqb.dqb_btime = 0;
79         if (q->dq_dqb.dqb_isoftlimit && q->dq_dqb.dqb_curinodes > q->dq_dqb.dqb_isoftlimit) {
80                 if (!q->dq_dqb.dqb_itime)
81                         q->dq_dqb.dqb_itime = now + q->dq_h->qh_info.dqi_igrace;
82         }
83         else
84                 q->dq_dqb.dqb_itime = 0;
85 }
86
87 /*
88  * Collect the requested quota information.
89  */
90 struct dquot *getprivs(qid_t id, struct quota_handle **handles, int quiet)
91 {
92         struct dquot *q, *qtail = NULL, *qhead = NULL;
93         int i;
94 #if defined(BSD_BEHAVIOUR)
95         int j, ngroups;
96         uid_t euid;
97         gid_t gidset[NGROUPS], *gidsetp;
98         char name[MAXNAMELEN];
99 #endif
100
101         for (i = 0; handles[i]; i++) {
102 #if defined(BSD_BEHAVIOUR)
103                 switch (handles[i]->qh_type) {
104                         case USRQUOTA:
105                                 euid = geteuid();
106                                 if (euid != id && euid != 0) {
107                                         uid2user(id, name);
108                                         errstr(_("%s (uid %d): Permission denied\n"), name, id);
109                                         return (struct dquot *)NULL;
110                                 }
111                                 break;
112                         case GRPQUOTA:
113                                 if (geteuid() == 0)
114                                         break;
115                                 ngroups = sysconf(_SC_NGROUPS_MAX);
116                                 if (ngroups > NGROUPS) {
117                                         gidsetp = malloc(ngroups * sizeof(gid_t));
118                                         if (!gidsetp) {
119                                                 gid2group(id, name);
120                                                 errstr(_("%s (gid %d): gid set allocation (%d): %s\n"), name, id, ngroups, strerror(errno));
121                                                 return (struct dquot *)NULL;
122                                         }
123                                 }
124                                 else
125                                         gidsetp = &gidset[0];
126                                 ngroups = getgroups(ngroups, gidsetp);
127                                 if (ngroups < 0) {
128                                         if (gidsetp != gidset)
129                                                 free(gidsetp);
130                                         gid2group(id, name);
131                                         errstr(_("%s (gid %d): error while trying getgroups(): %s\n"), name, id, strerror(errno));
132                                         return (struct dquot *)NULL;
133                                 }
134
135                                 for (j = 0; j < ngroups; j++)
136                                         if (id == gidsetp[j])
137                                                 break;
138                                 if (gidsetp != gidset)
139                                         free(gidsetp);
140                                 if (j >= ngroups) {
141                                         gid2group(id, name);
142                                         errstr(_("%s (gid %d): Permission denied\n"),
143                                                 name, id);
144                                         return (struct dquot *)NULL;
145                                 }
146                                 break;
147                         default:
148                                 break;
149                 }
150 #endif
151
152                 if (!(q = handles[i]->qh_ops->read_dquot(handles[i], id))) {
153                         /* If rpc.rquotad is not running filesystem might be just without quotas... */
154                         if (errno != ENOENT && (errno != ECONNREFUSED || !quiet)) {
155                                 int olderrno = errno;
156
157                                 id2name(id, handles[i]->qh_type, name);
158                                 errstr(_("error while getting quota from %s for %s (id %u): %s\n"),
159                                         handles[i]->qh_quotadev, name, id, strerror(olderrno));
160                         }
161                         continue;
162                 }
163                 if (qhead == NULL)
164                         qhead = q;
165                 else
166                         qtail->dq_next = q;
167                 qtail = q;
168                 q->dq_next = NULL;      /* This should be already set, but just for sure... */
169         }
170         return qhead;
171 }
172
173 /*
174  * Store the requested quota information.
175  */
176 int putprivs(struct dquot *qlist, int flags)
177 {
178         struct dquot *q;
179         int ret = 0;
180
181         for (q = qlist; q; q = q->dq_next) {
182                 if (q->dq_h->qh_ops->commit_dquot(q, flags) == -1) {
183                         errstr(_("Cannot write quota for %u on %s: %s\n"),
184                                 q->dq_id, q->dq_h->qh_quotadev, strerror(errno));
185                         ret = -1;
186                         continue;
187                 }
188         }
189         return ret;
190 }
191
192 /*
193  * Take a list of priviledges and get it edited.
194  */
195 #define MAX_ED_PARS 128
196 int editprivs(char *tmpfile)
197 {
198         sigset_t omask, nmask;
199         pid_t pid;
200         int stat;
201
202         sigemptyset(&nmask);
203         sigaddset(&nmask, SIGINT);
204         sigaddset(&nmask, SIGQUIT);
205         sigaddset(&nmask, SIGHUP);
206         sigprocmask(SIG_SETMASK, &nmask, &omask);
207         if ((pid = fork()) < 0) {
208                 errstr("Cannot fork(): %s\n", strerror(errno));
209                 return -1;
210         }
211         if (pid == 0) {
212                 char *ed, *actp, *nextp;
213                 char *edpars[MAX_ED_PARS];
214                 int i;
215
216                 sigprocmask(SIG_SETMASK, &omask, NULL);
217                 setgid(getgid());
218                 setuid(getuid());
219                 if (!(ed = getenv("VISUAL")))
220                         if (!(ed = getenv("EDITOR")))
221                                 ed = _PATH_VI;
222                 i = 0;
223                 ed = actp = sstrdup(ed);
224                 while (actp) {
225                         nextp = strchr(actp, ' ');
226                         if (nextp) {
227                                 *nextp = 0;
228                                 nextp++;
229                         }
230                         edpars[i++] = actp;
231                         if (i == MAX_ED_PARS-2) {
232                                 errstr(_("Too many parameters to editor.\n"));
233                                 break;
234                         }
235                         actp = nextp;
236                 }
237                 edpars[i++] = tmpfile;
238                 edpars[i] = NULL;
239                 execvp(edpars[0], edpars);
240                 die(1, _("Cannot exec %s\n"), ed);
241         }
242         waitpid(pid, &stat, 0);
243         sigprocmask(SIG_SETMASK, &omask, NULL);
244
245         return 0;
246 }
247
248 /*
249  * Convert a dquot list to an ASCII file.
250  */
251 int writeprivs(struct dquot *qlist, int outfd, char *name, int quotatype)
252 {
253         struct dquot *q;
254         FILE *fd;
255
256         ftruncate(outfd, 0);
257         lseek(outfd, 0, SEEK_SET);
258         if (!(fd = fdopen(dup(outfd), "w")))
259                 die(1, _("Cannot duplicate descriptor of file to write to: %s\n"), strerror(errno));
260
261 #if defined(ALT_FORMAT)
262         fprintf(fd, _("Disk quotas for %s %s (%cid %d):\n"),
263                 type2name(quotatype), name, *type2name(quotatype), qlist->dq_id);
264
265         fprintf(fd,
266                 _("  Filesystem                   blocks       soft       hard     inodes     soft     hard\n"));
267
268         for (q = qlist; q; q = q->dq_next) {
269                 fprintf(fd, "  %-24s %10llu %10llu %10llu %10llu %8llu %8llu\n",
270                         q->dq_h->qh_quotadev,
271                         (long long)toqb(q->dq_dqb.dqb_curspace),
272                         (long long)q->dq_dqb.dqb_bsoftlimit,
273                         (long long)q->dq_dqb.dqb_bhardlimit,
274                         (long long)q->dq_dqb.dqb_curinodes,
275                         (long long)q->dq_dqb.dqb_isoftlimit, (long long)q->dq_dqb.dqb_ihardlimit);
276         }
277 #else
278         fprintf(fd, _("Quotas for %s %s:\n"), type2name(quotatype), name);
279         for (q = qlist; q; q = q->dq_next) {
280                 fprintf(fd, _("%s %d, limits (soft = %d, hard = %d)\n"),
281                         q->dq_h->qh_quotadev, _("blocks in use:"),
282                         (int)toqb(q->dq_dqb.dqb_curspace),
283                         q->dq_dqb.dqb_bsoftlimit, q->dq_dqb.dqb_bhardlimit);
284                 fprintf(fd, _("%s %d, limits (soft = %d, hard = %d)\n"),
285                         _("\tinodes in use:"), q->dq_dqb.dqb_curinodes,
286                         q->dq_dqb.dqb_isoftlimit, q->dq_dqb.dqb_ihardlimit);
287         }
288 #endif
289         fclose(fd);
290         return 0;
291 }
292
293 /* Merge changes on one dev to proper structure in the list */
294 static void merge_limits_to_list(struct dquot *qlist, char *dev, u_int64_t blocks, u_int64_t bsoft,
295                           u_int64_t bhard, u_int64_t inodes, u_int64_t isoft, u_int64_t ihard)
296 {
297         struct dquot *q;
298
299         for (q = qlist; q; q = q->dq_next) {
300                 if (!devcmp_handle(dev, q->dq_h))
301                         continue;
302
303                 q->dq_dqb.dqb_bsoftlimit = bsoft;
304                 q->dq_dqb.dqb_bhardlimit = bhard;
305                 q->dq_dqb.dqb_isoftlimit = isoft;
306                 q->dq_dqb.dqb_ihardlimit = ihard;
307                 q->dq_flags |= DQ_FOUND;
308                 update_grace_times(q);
309
310                 if (blocks != toqb(q->dq_dqb.dqb_curspace))
311                         errstr(_("WARNING - %s: cannot change current block allocation\n"),
312                                 q->dq_h->qh_quotadev);
313                 if (inodes != q->dq_dqb.dqb_curinodes)
314                         errstr(_("WARNING - %s: cannot change current inode allocation\n"),
315                                 q->dq_h->qh_quotadev);
316         }
317 }
318
319 /*
320  * Merge changes to an ASCII file into a dquot list.
321  */
322 int readprivs(struct dquot *qlist, int infd)
323 {
324         FILE *fd;
325         int cnt;
326         long long blocks, bsoft, bhard, inodes, isoft, ihard;
327         struct dquot *q;
328
329 #if defined(ALT_FORMAT)
330         char fsp[BUFSIZ], line[BUFSIZ];
331 #else
332         char *fsp, line1[BUFSIZ], line2[BUFSIZ];
333 #endif
334
335         lseek(infd, 0, SEEK_SET);
336         if (!(fd = fdopen(dup(infd), "r")))
337                 die(1, _("Cannot duplicate descriptor of temp file: %s\n"), strerror(errno));
338
339 #if defined(ALT_FORMAT)
340         /*
341          * Discard title lines, then read lines to process.
342          */
343         fgets(line, sizeof(line), fd);
344         fgets(line, sizeof(line), fd);
345
346         while (fgets(line, sizeof(line), fd)) {
347                 cnt = sscanf(line, "%s %llu %llu %llu %llu %llu %llu",
348                              fsp, &blocks, &bsoft, &bhard, &inodes, &isoft, &ihard);
349
350                 if (cnt != 7) {
351                         errstr(_("Bad format:\n%s\n"), line);
352                         return -1;
353                 }
354
355                 merge_limits_to_list(qlist, fsp, blocks, bsoft, bhard, inodes, isoft, ihard);
356         }
357 #else
358         /*
359          * Discard title line, then read pairs of lines to process.
360          */
361         fgets(line1, sizeof(line1), fd);
362         while (fgets(line1, sizeof(line1), fd) && fgets(line2, sizeof(line2), fd)) {
363                 if (!(fsp = strtok(line1, " \t:"))) {
364                         errstr(_("%s - bad format\n"), line1);
365                         return -1;
366                 }
367                 if (!(cp = strtok(NULL, "\n"))) {
368                         errstr(_("%s -  %s -- bad format\n"),
369                                 fsp, &fsp[strlen(fsp) + 1]);
370                         return -1;
371                 }
372
373                 cnt = sscanf(cp, _(" blocks in use: %llu, limits (soft = %llu, hard = %llu)"),
374                              &blocks, &bsoft, &bhard);
375                 if (cnt != 3) {
376                         errstr(_("%s - %s -- bad format\n"),
377                                 fsp, cp);
378                         return -1;
379                 }
380
381                 if (!(cp = strtok(line2, "\n"))) {
382                         errstr(_("%s - %s -- bad format\n"),
383                                 fsp, line2);
384                         return -1;
385                 }
386
387                 cnt = sscanf(cp, _("\tinodes in use: %llu, limits (soft = %llu, hard = %llu)"),
388                              &inodes, &isoft, &ihard);
389                 if (cnt != 3) {
390                         errstr(_("%s - %s -- bad format\n"),
391                                 fsp, line2);
392                         return -1;
393                 }
394
395                 merge_limits_to_list(qlist, fsp, blocks, bsoft, bhard, inodes, isoft, ihard);
396         }
397 #endif
398         fclose(fd);
399
400         /*
401          * Disable quotas for any filesystems that have not been found.
402          */
403         for (q = qlist; q; q = q->dq_next) {
404                 if (q->dq_flags & DQ_FOUND) {
405                         q->dq_flags &= ~DQ_FOUND;
406                         continue;
407                 }
408                 q->dq_dqb.dqb_bsoftlimit = 0;
409                 q->dq_dqb.dqb_bhardlimit = 0;
410                 q->dq_dqb.dqb_isoftlimit = 0;
411                 q->dq_dqb.dqb_ihardlimit = 0;
412         }
413         return 0;
414 }
415
416 /* Merge changes on one dev to proper structure in the list */
417 static void merge_times_to_list(struct dquot *qlist, char *dev, time_t btime, time_t itime)
418 {
419         struct dquot *q;
420
421         for (q = qlist; q; q = q->dq_next) {
422                 if (!devcmp_handle(dev, q->dq_h))
423                         continue;
424
425                 q->dq_dqb.dqb_btime = btime;
426                 q->dq_dqb.dqb_itime = itime;
427                 q->dq_flags |= DQ_FOUND;
428         }
429 }
430
431 /*
432  * Write grace times of user to file
433  */
434 int writeindividualtimes(struct dquot *qlist, int outfd, char *name, int quotatype)
435 {
436         struct dquot *q;
437         FILE *fd;
438         time_t now;
439         char btimestr[MAXTIMELEN], itimestr[MAXTIMELEN];
440
441         ftruncate(outfd, 0);
442         lseek(outfd, 0, SEEK_SET);
443         if (!(fd = fdopen(dup(outfd), "w")))
444                 die(1, _("Cannot duplicate descriptor of file to write to: %s\n"), strerror(errno));
445
446         fprintf(fd, _("Times to enforce softlimit for %s %s (%cid %d):\n"),
447                 type2name(quotatype), name, *type2name(quotatype), qlist->dq_id);
448         fprintf(fd, _("Time units may be: days, hours, minutes, or seconds\n"));
449         fprintf(fd,
450                 _("  Filesystem                         block grace               inode grace\n"));
451
452         time(&now);
453         for (q = qlist; q; q = q->dq_next) {
454                 if (!q->dq_dqb.dqb_btime)
455                         strcpy(btimestr, _("unset"));
456                 else if (q->dq_dqb.dqb_btime <= now)
457                         strcpy(btimestr, _("0seconds"));
458                 else
459                         sprintf(btimestr, "%useconds", (unsigned)(q->dq_dqb.dqb_btime - now));
460                 if (!q->dq_dqb.dqb_itime)
461                         strcpy(itimestr, _("unset"));
462                 else if (q->dq_dqb.dqb_itime <= now)
463                         strcpy(itimestr, _("0seconds"));
464                 else
465                         sprintf(itimestr, _("%useconds"), (unsigned)(q->dq_dqb.dqb_itime - now));
466
467                 fprintf(fd, "  %-24s %22s %22s\n", q->dq_h->qh_quotadev, btimestr, itimestr);
468         }
469         fclose(fd);
470         return 0;
471 }
472
473 /*
474  *  Read list of grace times for a user and convert it
475  */
476 int readindividualtimes(struct dquot *qlist, int infd)
477 {
478         FILE *fd;
479         int cnt, btime, itime;
480         char line[BUFSIZ], fsp[BUFSIZ], btimestr[BUFSIZ], itimestr[BUFSIZ];
481         char iunits[BUFSIZ], bunits[BUFSIZ];
482         time_t now, bseconds, iseconds;
483
484         lseek(infd, 0, SEEK_SET);
485         if (!(fd = fdopen(dup(infd), "r")))
486                 die(1, _("Cannot duplicate descriptor of temp file: %s\n"), strerror(errno));
487
488         /*
489          * Discard title lines, then read lines to process.
490          */
491         fgets(line, sizeof(line), fd);
492         fgets(line, sizeof(line), fd);
493         fgets(line, sizeof(line), fd);
494
495         time(&now);
496         while (fgets(line, sizeof(line), fd)) {
497                 cnt = sscanf(line, "%s %s %s", fsp, btimestr, itimestr);
498                 if (cnt != 3) {
499 format_err:
500                         errstr(_("bad format:\n%s\n"), line);
501                         return -1;
502                 }
503                 if (!strcmp(btimestr, _("unset")))
504                         bseconds = 0;
505                 else {
506                         if (sscanf(btimestr, "%d%s", &btime, bunits) != 2)
507                                 goto format_err;
508                         if (str2timeunits(btime, bunits, &bseconds) < 0) {
509 units_err:
510                                 errstr(_("Bad time units. Units are 'second', 'minute', 'hour', and 'day'.\n"));
511                                 return -1;
512                         }
513                         bseconds += now;
514                 }
515                 if (!strcmp(itimestr, _("unset")))
516                         iseconds = 0;
517                 else {
518                         if (sscanf(itimestr, "%d%s", &itime, iunits) != 2)
519                                 goto format_err;
520                         if (str2timeunits(itime, iunits, &iseconds) < 0)
521                                 goto units_err;
522                         iseconds += now;
523                 }
524                 merge_times_to_list(qlist, fsp, bseconds, iseconds);
525         }
526         fclose(fd);
527
528         return 0;
529 }
530
531 /*
532  * Convert a dquot list to an ASCII file of grace times.
533  */
534 int writetimes(struct quota_handle **handles, int outfd)
535 {
536         FILE *fd;
537         char itimebuf[MAXTIMELEN], btimebuf[MAXTIMELEN];
538         int i;
539
540         if (!handles[0])
541                 return 0;
542
543         ftruncate(outfd, 0);
544         lseek(outfd, 0, SEEK_SET);
545         if ((fd = fdopen(dup(outfd), "w")) == NULL)
546                 die(1, _("Cannot duplicate descriptor of file to edit: %s\n"), strerror(errno));
547
548 #if defined(ALT_FORMAT)
549         fprintf(fd, _("Grace period before enforcing soft limits for %ss:\n"),
550                 type2name(handles[0]->qh_type));
551         fprintf(fd, _("Time units may be: days, hours, minutes, or seconds\n"));
552         fprintf(fd, _("  Filesystem             Block grace period     Inode grace period\n"));
553
554         for (i = 0; handles[i]; i++) {
555                 time2str(handles[i]->qh_info.dqi_bgrace, btimebuf, 0);
556                 time2str(handles[i]->qh_info.dqi_igrace, itimebuf, 0);
557                 fprintf(fd, "  %-12s %22s %22s\n", handles[i]->qh_quotadev, btimebuf, itimebuf);
558         }
559 #else
560         fprintf(fd, _("Time units may be: days, hours, minutes, or seconds\n"));
561         fprintf(fd, _("Grace period before enforcing soft limits for %ss:\n"),
562                 type2name(handles[0]->qh_type));
563         for (i = 0; handles[i]; i++) {
564                 time2str(handles[i]->qh_info.dqi_bgrace, btimebuf, 0);
565                 time2str(handles[i]->qh_info.dqi_igrace, itimebuf, 0);
566                 fprintf(fd, _("block grace period: %s, file grace period: %s\n"),
567                         handles[i]->qh_quotadev, btimebuf, itimebuf);
568         }
569 #endif
570
571         fclose(fd);
572         return 0;
573 }
574
575 /*
576  * Merge changes of grace times in an ASCII file into a dquot list.
577  */
578 int readtimes(struct quota_handle **handles, int infd)
579 {
580         FILE *fd;
581         int itime, btime, i, cnt;
582         time_t iseconds, bseconds;
583
584 #if defined(ALT_FORMAT)
585         char fsp[BUFSIZ], bunits[10], iunits[10], line[BUFSIZ];
586 #else
587         char *fsp, bunits[10], iunits[10], line1[BUFSIZ];
588 #endif
589
590         if (!handles[0])
591                 return 0;
592         lseek(infd, 0, SEEK_SET);
593         if (!(fd = fdopen(dup(infd), "r"))) {
594                 errstr(_("Cannot reopen temp file: %s\n"),
595                         strerror(errno));
596                 return -1;
597         }
598
599         /* Set all grace times to default values */
600         for (i = 0; handles[i]; i++) {
601                 handles[i]->qh_info.dqi_bgrace = MAX_DQ_TIME;
602                 handles[i]->qh_info.dqi_igrace = MAX_IQ_TIME;
603                 mark_quotafile_info_dirty(handles[i]);
604         }
605 #if defined(ALT_FORMAT)
606         /*
607          * Discard three title lines, then read lines to process.
608          */
609         fgets(line, sizeof(line), fd);
610         fgets(line, sizeof(line), fd);
611         fgets(line, sizeof(line), fd);
612
613         while (fgets(line, sizeof(line), fd)) {
614                 cnt = sscanf(line, "%s %d %s %d %s", fsp, &btime, bunits, &itime, iunits);
615                 if (cnt != 5) {
616                         errstr(_("bad format:\n%s\n"), line);
617                         return -1;
618                 }
619 #else
620         /*
621          * Discard two title lines, then read lines to process.
622          */
623         fgets(line1, sizeof(line1), fd);
624         fgets(line1, sizeof(line1), fd);
625
626         while (fgets(line1, sizeof(line1), fd)) {
627                 if (!(fsp = strtok(line1, " \t:"))) {
628                         errstr(_("%s - bad format\n"), line1);
629                         return -1;
630                 }
631                 if (!(cp = strtok(NULL, "\n"))) {
632                         errstr(_("%s - %s -- bad format\n"),
633                                 fsp, &fsp[strlen(fsp) + 1]);
634                         return -1;
635                 }
636                 cnt = sscanf(cp, _(" block grace period: %d %s file grace period: %d %s"),
637                              &btime, bunits, &itime, iunits);
638                 if (cnt != 4) {
639                         errstr(_("%s - %s -- bad format\n"),
640                                 fsp, cp);
641                         return -1;
642                 }
643 #endif
644                 if (str2timeunits(btime, bunits, &bseconds) < 0 ||
645                     str2timeunits(itime, iunits, &iseconds) < 0) {
646                         errstr(_("Bad time units. Units are 'second', 'minute', 'hour', and 'day'.\n"));
647                         return -1;
648                 }
649                 for (i = 0; handles[i]; i++) {
650                         if (!devcmp_handle(fsp, handles[i]))
651                                 continue;
652                         handles[i]->qh_info.dqi_bgrace = bseconds;
653                         handles[i]->qh_info.dqi_igrace = iseconds;
654                         mark_quotafile_info_dirty(handles[i]);
655                         break;
656                 }
657         }
658         fclose(fd);
659
660         return 0;
661 }
662
663 /*
664  * Free a list of dquot structures.
665  */
666 void freeprivs(struct dquot *qlist)
667 {
668         struct dquot *q, *nextq;
669
670         for (q = qlist; q; q = nextq) {
671                 nextq = q->dq_next;
672                 free(q);
673         }
674 }