Initial commit to Gerrit
[profile/ivi/quota.git] / quotasys.c
1 /*
2  *
3  *      Interactions of quota with system - filenames, fstab and so on...
4  *
5  *      Jan Kara <jack@suse.cz> - sponsored by SuSE CR
6  */
7
8 #include "config.h"
9
10 #include <stdio.h>
11 #include <errno.h>
12 #include <string.h>
13 #include <limits.h>
14 #include <pwd.h>
15 #include <grp.h>
16 #include <stdlib.h>
17 #include <time.h>
18 #include <unistd.h>
19 #include <fcntl.h>
20 #include <signal.h>
21 #include <ctype.h>
22 #include <paths.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <sys/vfs.h>
26
27 #include "pot.h"
28 #include "bylabel.h"
29 #include "common.h"
30 #include "quotasys.h"
31 #include "quotaio.h"
32 #include "dqblk_v1.h"
33 #include "dqblk_v2.h"
34 #include "dqblk_xfs.h"
35 #include "quotaio_v2.h"
36
37 #define min(x,y) (((x) < (y)) ? (x) : (y))
38
39 #define QFMT_NAMES 5
40
41 static char extensions[MAXQUOTAS + 2][20] = INITQFNAMES;
42 static char *basenames[] = INITQFBASENAMES;
43 static char *fmtnames[] = { "vfsold",
44                             "vfsv0",
45                             "vfsv1",
46                             "rpc",
47                             "xfs",
48 };
49
50 /*
51  *      Check for various kinds of NFS filesystem
52  */
53 int nfs_fstype(char *type)
54 {
55         return !strcmp(type, MNTTYPE_NFS) || !strcmp(type, MNTTYPE_NFS4) ||
56                 !strcmp(type, MNTTYPE_MPFS);
57 }
58
59 /*
60  *      Check whether filesystem has hidden quota files which is handles
61  *      as metadata (and thus always tracks usage).
62  */
63 int meta_qf_fstype(char *type)
64 {
65         return !strcmp(type, MNTTYPE_OCFS2);
66 }
67
68 /*
69  *      Check whether give filesystem type is supported
70  */
71
72 static int correct_fstype(char *type)
73 {
74         char *mtype = sstrdup(type), *next;
75
76         type = mtype;
77         do {
78                 next = strchr(type, ',');
79                 if (next)
80                         *next = 0;
81                 if (!strcmp(type, MNTTYPE_EXT2) ||
82                     !strcmp(type, MNTTYPE_EXT3) ||
83                     !strcmp(type, MNTTYPE_EXT4) ||
84                     !strcmp(type, MNTTYPE_EXT4DEV) ||
85                     !strcmp(type, MNTTYPE_JFS) ||
86                     !strcmp(type, MNTTYPE_MINIX) ||
87                     !strcmp(type, MNTTYPE_UFS) ||
88                     !strcmp(type, MNTTYPE_UDF) ||
89                     !strcmp(type, MNTTYPE_REISER) ||
90                     !strcmp(type, MNTTYPE_XFS) ||
91                     !strcmp(type, MNTTYPE_NFS) ||
92                     !strcmp(type, MNTTYPE_NFS4) ||
93                     !strcmp(type, MNTTYPE_OCFS2) ||
94                     !strcmp(type, MNTTYPE_MPFS)) {
95                         free(mtype);
96                         return 1;
97                 }
98                 type = next+1;  
99         } while (next);
100         free(mtype);
101         return 0;
102 }
103
104 /*
105  *      Convert type of quota to written representation
106  */
107 char *type2name(int type)
108 {
109         return extensions[type];
110 }
111
112 /*
113  *      Convert name to uid
114  */
115 uid_t user2uid(char *name, int flag, int *err)
116 {
117         struct passwd *entry;
118         uid_t ret;
119         char *errch;
120
121         if (err)
122                 *err = 0;
123         if (!flag) {
124                 ret = strtoul(name, &errch, 0);
125                 if (!*errch)            /* Is name number - we got directly uid? */
126                         return ret;
127         }
128         if (!(entry = getpwnam(name))) {
129                 if (!err) {
130                         errstr(_("user %s does not exist.\n"), name);
131                         exit(1);
132                 }
133                 else {
134                         *err = -1;
135                         return 0;
136                 }
137         }
138         return entry->pw_uid;
139 }
140
141 /*
142  *      Convert group name to gid
143  */
144 gid_t group2gid(char *name, int flag, int *err)
145 {
146         struct group *entry;
147         gid_t ret;
148         char *errch;
149
150         if (err)
151                 *err = 0;
152         if (!flag) {
153                 ret = strtoul(name, &errch, 0);
154                 if (!*errch)            /* Is name number - we got directly gid? */
155                         return ret;
156         }
157         if (!(entry = getgrnam(name))) {
158                 if (!err) {
159                         errstr(_("group %s does not exist.\n"), name);
160                         exit(1);
161                 }
162                 else {
163                         *err = -1;
164                         return 0;
165                 }
166         }
167         return entry->gr_gid;
168 }
169
170 /*
171  *      Convert name to id
172  */
173 int name2id(char *name, int qtype, int flag, int *err)
174 {
175         if (qtype == USRQUOTA)
176                 return user2uid(name, flag, err);
177         else
178                 return group2gid(name, flag, err);
179 }
180
181 /*
182  *      Convert uid to name
183  */
184 int uid2user(uid_t id, char *buf)
185 {
186         struct passwd *entry;
187
188         if (!(entry = getpwuid(id))) {
189                 snprintf(buf, MAXNAMELEN, "#%u", (uint) id);
190                 return 1;
191         }
192         else
193                 sstrncpy(buf, entry->pw_name, MAXNAMELEN);
194         return 0;
195 }
196
197 /*
198  *      Convert gid to name
199  */
200 int gid2group(gid_t id, char *buf)
201 {
202         struct group *entry;
203
204         if (!(entry = getgrgid(id))) {
205                 snprintf(buf, MAXNAMELEN, "#%u", (uint) id);
206                 return 1;
207         }
208         else
209                 sstrncpy(buf, entry->gr_name, MAXNAMELEN);
210         return 0;
211 }
212
213 /*
214  *      Convert id to user/groupname
215  */
216 int id2name(int id, int qtype, char *buf)
217 {
218         if (qtype == USRQUOTA)
219                 return uid2user(id, buf);
220         else
221                 return gid2group(id, buf);
222 }
223
224 /*
225  *      Parse /etc/nsswitch.conf and return type of default passwd handling
226  */
227 int passwd_handling(void)
228 {
229         FILE *f;
230         char buf[1024], *colpos, *spcpos;
231         int ret = PASSWD_FILES;
232
233         if (!(f = fopen("/etc/nsswitch.conf", "r")))
234                 return PASSWD_FILES;    /* Can't open nsswitch.conf - fallback on compatible mode */
235         while (fgets(buf, sizeof(buf), f)) {
236                 if (strncmp(buf, "passwd:", 7)) /* Not passwd entry? */
237                         continue;
238                 for (colpos = buf+7; isspace(*colpos); colpos++);
239                 if (!*colpos)   /* Not found any type of handling? */
240                         break;
241                 for (spcpos = colpos; !isspace(*spcpos) && *spcpos; spcpos++);
242                 *spcpos = 0;
243                 if (!strcmp(colpos, "db") || !strcmp(colpos, "nis") || !strcmp(colpos, "nis+"))
244                         ret = PASSWD_DB;
245                 break;
246         }
247         fclose(f);
248         return ret;
249 }
250
251 /*
252  *      Convert quota format name to number
253  */
254 int name2fmt(char *str)
255 {
256         int fmt;
257
258         for (fmt = 0; fmt < QFMT_NAMES; fmt++)
259                 if (!strcmp(str, fmtnames[fmt]))
260                         return fmt;
261         errstr(_("Unknown quota format: %s\nSupported formats are:\n\
262   vfsold - original quota format\n\
263   vfsv0 - standard quota format\n\
264   vfsv1 - quota format with 64-bit limits\n\
265   rpc - use RPC calls\n\
266   xfs - XFS quota format\n"), str);
267         return QF_ERROR;
268 }
269
270 /*
271  *      Convert quota format number to name
272  */
273 char *fmt2name(int fmt)
274 {
275         return fmtnames[fmt];
276 }
277
278 /*
279  *      Convert kernel to utility quota format number
280  */
281 int kern2utilfmt(int kernfmt)
282 {
283         switch (kernfmt) {
284                 case QFMT_VFS_OLD:
285                         return QF_VFSOLD;
286                 case QFMT_VFS_V0:
287                         return QF_VFSV0;
288                 case QFMT_VFS_V1:
289                         return QF_VFSV1;
290                 case QFMT_OCFS2:
291                         return QF_META;
292         }
293         return -1;
294 }
295
296 /*
297  *      Convert utility to kernel quota format number
298  */
299 int util2kernfmt(int fmt)
300 {
301         switch (fmt) {
302                 case QF_VFSOLD:
303                         return QFMT_VFS_OLD;
304                 case QF_VFSV0:
305                         return QFMT_VFS_V0;
306                 case QF_VFSV1:
307                         return QFMT_VFS_V1;
308         }
309         return -1;
310 }
311
312 /*
313  * Convert time difference of seconds and current time
314  */
315 void difftime2str(time_t seconds, char *buf)
316 {
317         time_t now;
318
319         buf[0] = 0;
320         if (!seconds)
321                 return;
322         time(&now);
323         if (seconds <= now) {
324                 strcpy(buf, _("none"));
325                 return;
326         }
327         time2str(seconds - now, buf, TF_ROUND);
328 }
329
330 /*
331  * Convert time to printable form
332  */
333 void time2str(time_t seconds, char *buf, int flags)
334 {
335         uint minutes, hours, days;
336
337         if (flags & TF_ROUND) {
338                 minutes = (seconds + 30) / 60;  /* Rounding */
339                 hours = minutes / 60;
340                 minutes %= 60;
341                 days = hours / 24;
342                 hours %= 24;
343                 if (days >= 2)
344                         snprintf(buf, MAXTIMELEN, _("%ddays"), days);
345                 else
346                         snprintf(buf, MAXTIMELEN, _("%02d:%02d"), hours + days * 24, minutes);
347         }
348         else {
349                 minutes = seconds / 60;
350                 seconds %= 60;
351                 hours = minutes / 60;
352                 minutes %= 60;
353                 days = hours / 24;
354                 hours %= 24;
355                 if (seconds || (!minutes && !hours && !days))
356                         snprintf(buf, MAXTIMELEN, _("%useconds"), (uint)(seconds+minutes*60+hours*3600+days*3600*24));
357                 else if (minutes)
358                         snprintf(buf, MAXTIMELEN, _("%uminutes"), (uint)(minutes+hours*60+days*60*24));
359                 else if (hours)
360                         snprintf(buf, MAXTIMELEN, _("%uhours"), (uint)(hours+days*24));
361                 else
362                         snprintf(buf, MAXTIMELEN, _("%udays"), days);
363         }
364 }
365
366 /*
367  * Convert number with unit to time in seconds
368  */
369 int str2timeunits(time_t num, char *unit, time_t *res)
370 {
371         if (memcmp(unit, "second", 6) == 0)
372                 *res = num;
373         else if (memcmp(unit, "minute", 6) == 0)
374                 *res = num * 60;
375         else if (memcmp(unit, "hour", 4) == 0)
376                 *res = num * 60 * 60;
377         else if (memcmp(unit, "day", 3) == 0)
378                 *res = num * 24 * 60 * 60;
379         else
380                 return -1;
381         return 0;
382 }
383
384 /*
385  * Convert number in quota blocks to some nice short form for printing
386  */
387 void space2str(qsize_t space, char *buf, int format)
388 {
389         int i;
390         char suffix[8] = " MGT";
391
392         space = qb2kb(space);
393         if (format) {
394                 for (i = 3; i > 0; i--)
395                         if (space >= (1LL << (QUOTABLOCK_BITS*i))*100) {
396                                 sprintf(buf, "%llu%c", (unsigned long long)(space+(1 << (QUOTABLOCK_BITS*i))-1) >> (QUOTABLOCK_BITS*i), suffix[i]);
397                                 return;
398                         }
399                 sprintf(buf, "%lluK", (unsigned long long)space);
400                 return;
401         }
402         sprintf(buf, "%llu", (unsigned long long)space);
403 }
404
405 /*
406  *  Convert number to some nice short form for printing
407  */
408 void number2str(unsigned long long num, char *buf, int format)
409 {
410         int i;
411         unsigned long long div;
412         char suffix[8] = " kmgt";
413
414         if (format)
415                 for (i = 4, div = 1000000000000LL; i > 0; i--, div /= 1000)
416                         if (num >= 100*div) {
417                                 sprintf(buf, "%llu%c", (num+div-1) / div, suffix[i]);
418                                 return;
419                         }
420         sprintf(buf, "%llu", num);
421 }
422
423 /*
424  *      Check for XFS filesystem with quota accounting enabled
425  */
426 static int hasxfsquota(struct mntent *mnt, int type, int flags)
427 {
428         int ret = 0;
429         u_int16_t sbflags;
430         struct xfs_mem_dqinfo info;
431         const char *dev;
432         char *opt, *endopt;
433
434         if (flags & MS_XFS_DISABLED)
435                 return 1;
436
437         dev = get_device_name(mnt->mnt_fsname);
438         if (!dev)
439                 return 0;
440         /* Loopback mounted device with a loopback device in the arguments? */
441         if ((opt = hasmntopt(mnt, MNTOPT_LOOP)) && (opt = strchr(opt, '='))) {
442                 free((char *)dev);
443                 endopt = strchr(opt+1, ',');
444                 if (!endopt)
445                         dev = strdup(opt+1);
446                 else
447                         dev = strndup(opt+1, endopt-opt-1);
448                 if (!dev)
449                         return 0;
450         }
451
452         memset(&info, 0, sizeof(struct xfs_mem_dqinfo));
453         if (!quotactl(QCMD(Q_XFS_GETQSTAT, type), dev, 0, (void *)&info)) {
454                 sbflags = (info.qs_flags & 0xff00) >> 8;
455                 if (type == USRQUOTA && (info.qs_flags & XFS_QUOTA_UDQ_ACCT))
456                         ret = 1;
457                 else if (type == GRPQUOTA && (info.qs_flags & XFS_QUOTA_GDQ_ACCT))
458                         ret = 1;
459 #ifdef XFS_ROOTHACK
460                 /*
461                  * Old XFS filesystems (up to XFS 1.2 / Linux 2.5.47) had a
462                  * hack to allow enabling quota on the root filesystem without
463                  * having to specify it at mount time.
464                  */
465                 else if (strcmp(mnt->mnt_dir, "/"))
466                         ret = 0;
467                 else if (type == USRQUOTA && (sbflags & XFS_QUOTA_UDQ_ACCT))
468                         ret = 1;
469                 else if (type == GRPQUOTA && (sbflags & XFS_QUOTA_GDQ_ACCT))
470                         ret = 1;
471 #endif /* XFS_ROOTHACK */
472         }
473         free((char *)dev);
474         return ret;
475 }
476
477 /* Return if given option has nonempty argument */
478 char *hasmntoptarg(struct mntent *mnt, char *opt)
479 {
480         char *p = hasmntopt(mnt, opt);
481
482         if (!p)
483                 return NULL;
484         p += strlen(opt);
485         if (*p == '=' && p[1] != ',')
486                 return p+1;
487         return NULL;
488 }
489
490 /*
491  *      Check to see if a particular quota is to be enabled (filesystem mounted with proper option)
492  */
493 int hasquota(struct mntent *mnt, int type, int flags)
494 {
495         if (!correct_fstype(mnt->mnt_type) || hasmntopt(mnt, MNTOPT_NOQUOTA))
496                 return 0;
497         
498         if (!strcmp(mnt->mnt_type, MNTTYPE_XFS))
499                 return hasxfsquota(mnt, type, flags);
500         if (nfs_fstype(mnt->mnt_type))  /* NFS always has quota or better there is no good way how to detect it */
501                 return 1;
502
503         if ((type == USRQUOTA) && (hasmntopt(mnt, MNTOPT_USRQUOTA) || hasmntoptarg(mnt, MNTOPT_USRJQUOTA)))
504                 return 1;
505         if ((type == GRPQUOTA) && (hasmntopt(mnt, MNTOPT_GRPQUOTA) || hasmntoptarg(mnt, MNTOPT_GRPJQUOTA)))
506                 return 1;
507         if ((type == USRQUOTA) && hasmntopt(mnt, MNTOPT_QUOTA))
508                 return 1;
509         return 0;
510 }
511
512 /* Check whether quotafile for given format exists - return its name in namebuf */
513 static int check_fmtfile_ok(char *name, int type, int fmt, int flags)
514 {
515         if (!flags)
516                 return 1;
517         if (flags & NF_EXIST) {
518                 struct stat st;
519
520                 if (stat(name, &st) < 0) {
521                         if (errno != ENOENT)
522                                 errstr(_("Cannot stat quota file %s: %s\n"), name, strerror(errno));
523                         return 0;
524                 }
525         } 
526         if (flags & NF_FORMAT) {
527                 int fd, ret = 0;
528
529                 if ((fd = open(name, O_RDONLY)) >= 0) {
530                         if (is_tree_qfmt(fmt))
531                                 ret = quotafile_ops_2.check_file(fd, type, fmt);
532                         else
533                                 ret = quotafile_ops_1.check_file(fd, type, fmt);
534                         close(fd);
535                         if (ret <= 0)
536                                 return 0;
537                 }
538                 else if (errno != ENOENT && errno != EPERM) {
539                         errstr(_("Cannot open quotafile %s: %s\n"), name, strerror(errno));
540                         return 0;
541                 }
542         }
543         return 1;
544 }
545
546 /*
547  *      Get quotafile name for given entry. Return 0 in case format check succeeded,
548  *      otherwise return -1.
549  *      Note that formats without quotafile *must* be detected prior to calling this function
550  */
551 int get_qf_name(struct mntent *mnt, int type, int fmt, int flags, char **filename)
552 {
553         char *option, *pathname, has_quota_file_definition = 0;
554         char qfullname[PATH_MAX];
555
556         qfullname[0] = 0;
557         if (type == USRQUOTA && (option = hasmntopt(mnt, MNTOPT_USRQUOTA))) {
558                 if (*(pathname = option + strlen(MNTOPT_USRQUOTA)) == '=')
559                         has_quota_file_definition = 1;
560         }
561         else if (type == USRQUOTA && (option = hasmntoptarg(mnt, MNTOPT_USRJQUOTA))) {
562                 pathname = option;
563                 has_quota_file_definition = 1;
564                 sstrncpy(qfullname, mnt->mnt_dir, sizeof(qfullname));
565                 sstrncat(qfullname, "/", sizeof(qfullname));
566         }
567         else if (type == GRPQUOTA && (option = hasmntopt(mnt, MNTOPT_GRPQUOTA))) {
568                 pathname = option + strlen(MNTOPT_GRPQUOTA);
569                 if (*pathname == '=') {
570                         has_quota_file_definition = 1;
571                         pathname++;
572                 }
573         }
574         else if (type == GRPQUOTA && (option = hasmntoptarg(mnt, MNTOPT_GRPJQUOTA))) {
575                 pathname = option;
576                 has_quota_file_definition = 1;
577                 sstrncpy(qfullname, mnt->mnt_dir, sizeof(qfullname));
578                 sstrncat(qfullname, "/", sizeof(qfullname));
579         }
580         else if (type == USRQUOTA && (option = hasmntopt(mnt, MNTOPT_QUOTA))) {
581                 pathname = option + strlen(MNTOPT_QUOTA);
582                 if (*pathname == '=') {
583                         has_quota_file_definition = 1;
584                         pathname++;
585                 }
586         }
587         else
588                 return -1;
589
590         if (has_quota_file_definition) {
591                 if ((option = strchr(pathname, ','))) {
592                         int tocopy = min(option - pathname + 1,
593                                          sizeof(qfullname) - strlen(qfullname));
594                         sstrncpy(qfullname + strlen(qfullname), pathname, tocopy);
595                 } else
596                         sstrncat(qfullname, pathname, sizeof(qfullname));
597         } else {
598                 snprintf(qfullname, PATH_MAX, "%s/%s.%s", mnt->mnt_dir,
599                          basenames[fmt], extensions[type]);
600         }
601         if (check_fmtfile_ok(qfullname, type, fmt, flags)) {
602                 *filename = sstrdup(qfullname);
603                 return 0;
604         }
605         return -1;
606 }
607
608 #define START_MNT_POINTS 256    /* The number of mount points we start with... */
609
610 /*
611  *      Create NULL terminated list of quotafile handles from given list of mountpoints
612  *      List of zero length means scan all entries in /etc/mtab
613  */
614 struct quota_handle **create_handle_list(int count, char **mntpoints, int type, int fmt,
615                                          int ioflags, int mntflags)
616 {
617         struct mntent *mnt;
618         int gotmnt = 0;
619         static int hlist_allocated = 0;
620         static struct quota_handle **hlist = NULL;
621
622         if (!hlist_allocated) {
623                 hlist = smalloc(START_MNT_POINTS * sizeof(struct quota_handle *));
624                 hlist_allocated = START_MNT_POINTS;
625         }
626
627         /* If directories are specified, cache all NFS mountpoints */
628         if (count && !(mntflags & MS_LOCALONLY))
629                 mntflags |= MS_NFS_ALL;
630
631         if (init_mounts_scan(count, mntpoints, mntflags) < 0)
632                 die(2, _("Cannot initialize mountpoint scan.\n"));
633         while ((mnt = get_next_mount())) {
634 #ifndef RPC
635                 if (nfs_fstype(mnt->mnt_type))
636                         continue;
637 #endif
638                 if (fmt == -1 || count) {
639 add_entry:
640                         if (gotmnt+1 >= hlist_allocated) {
641                                 hlist_allocated += START_MNT_POINTS;
642                                 hlist = srealloc(hlist, hlist_allocated * sizeof(struct quota_handle *));
643                         }
644                         if (!(hlist[gotmnt] = init_io(mnt, type, fmt, ioflags)))
645                                 continue;
646                         gotmnt++;
647                 }
648                 else {
649                         switch (fmt) {
650                         case QF_RPC:
651                                 if (nfs_fstype(mnt->mnt_type))
652                                         goto add_entry;
653                                 break;
654                         case QF_XFS:
655                                 if (!strcmp(mnt->mnt_type, MNTTYPE_XFS))
656                                         goto add_entry;
657                                 break;
658                         default:
659                                 if (strcmp(mnt->mnt_type, MNTTYPE_XFS) && !nfs_fstype(mnt->mnt_type))
660                                         goto add_entry;
661                                 break;
662                         }
663                 }
664         }
665         end_mounts_scan();
666         hlist[gotmnt] = NULL;
667         if (count && gotmnt != count)
668                 die(1, _("Not all specified mountpoints are using quota.\n"));
669         return hlist;
670 }
671
672 /*
673  *      Free given list of handles
674  */
675 int dispose_handle_list(struct quota_handle **hlist)
676 {
677         int i;
678         int ret = 0;
679
680         for (i = 0; hlist[i]; i++)
681                 if (end_io(hlist[i]) < 0) {
682                         errstr(_("Error while releasing file on %s\n"),
683                                 hlist[i]->qh_quotadev);
684                         ret = -1;
685                 }
686         return ret;
687 }
688
689 /*
690  *      Check whether given device name matches this quota handle
691  */
692 int devcmp_handle(const char *dev, struct quota_handle *h)
693 {
694         struct stat sbuf;
695
696         if (stat(dev, &sbuf) < 0)
697                 return (strcmp(dev, h->qh_quotadev) == 0);
698         if (!S_ISBLK(sbuf.st_mode))
699                 return (strcmp(dev, h->qh_quotadev) == 0);
700         if (sbuf.st_rdev != h->qh_stat.st_rdev)
701                 return 0;
702         return 1;
703 }
704
705 /*
706  *      Check whether two quota handles are for the same device
707  */
708 int devcmp_handles(struct quota_handle *a, struct quota_handle *b)
709 {
710         if (!S_ISBLK(a->qh_stat.st_mode) || !S_ISBLK(b->qh_stat.st_mode))
711                 return (strcmp(a->qh_quotadev, b->qh_quotadev) == 0);
712         if (a->qh_stat.st_rdev != b->qh_stat.st_rdev)
713                 return 0;
714         return 1;
715 }
716
717 /*
718  *      Check kernel quota version
719  */
720
721 int kernel_iface;       /* Kernel interface type */
722 static int kernel_qfmt_num;     /* Number of different supported formats */
723 static int kernel_qfmt[QUOTAFORMATS]; /* Formats supported by kernel */
724
725 #ifndef FS_DQSTATS
726 #define FS_DQSTATS 16
727 #endif
728 #ifndef FS_DQ_SYNCS
729 #define FS_DQ_SYNCS 8
730 #endif
731
732 void init_kernel_interface(void)
733 {
734         struct stat st;
735         struct sigaction sig, oldsig;
736         
737         /* This signal handling is needed because old kernels send us SIGSEGV as they try to resolve the device */
738         sig.sa_handler = SIG_IGN;
739         sig.sa_sigaction = NULL;
740         if (sigemptyset(&sig.sa_mask) < 0)
741                 die(2, _("Cannot create set for sigaction(): %s\n"), strerror(errno));
742         sig.sa_flags = 0;
743         if (sigaction(SIGSEGV, &sig, &oldsig) < 0)
744                 die(2, _("Cannot set signal handler: %s\n"), strerror(errno));
745
746         kernel_qfmt_num = 0;
747         if (!stat("/proc/fs/xfs/stat", &st))
748                 kernel_qfmt[kernel_qfmt_num++] = QF_XFS;
749         else
750                 if (!quotactl(QCMD(Q_XGETQSTAT, 0), NULL, 0, NULL) || (errno != EINVAL && errno != ENOSYS))
751                         kernel_qfmt[kernel_qfmt_num++] = QF_XFS;
752         /* Detect new kernel interface; Assume generic interface unless we can prove there is not one... */
753         if (!stat("/proc/sys/fs/quota", &st) || errno != ENOENT) {
754                 kernel_iface = IFACE_GENERIC;
755                 kernel_qfmt[kernel_qfmt_num++] = QF_META;
756                 kernel_qfmt[kernel_qfmt_num++] = QF_VFSOLD;
757                 kernel_qfmt[kernel_qfmt_num++] = QF_VFSV0;
758                 kernel_qfmt[kernel_qfmt_num++] = QF_VFSV1;
759         }
760         else {
761                 struct v2_dqstats v2_stats;
762
763                 if (quotactl(QCMD(Q_V2_GETSTATS, 0), NULL, 0, (void *)&v2_stats) >= 0) {
764                         kernel_qfmt[kernel_qfmt_num++] = QF_VFSV0;
765                         kernel_iface = IFACE_VFSV0;
766                 }
767                 else if (errno != ENOSYS && errno != ENOTSUP) {
768                         /* RedHat 7.1 (2.4.2-2) newquota check 
769                          * Q_V2_GETSTATS in it's old place, Q_GETQUOTA in the new place
770                          * (they haven't moved Q_GETSTATS to its new value) */
771                         int err_stat = 0;
772                         int err_quota = 0;
773                         char tmp[1024];         /* Just temporary buffer */
774
775                         if (quotactl(QCMD(Q_V1_GETSTATS, 0), NULL, 0, tmp))
776                                 err_stat = errno;
777                         if (quotactl(QCMD(Q_V1_GETQUOTA, 0), "/dev/null", 0, tmp))
778                                 err_quota = errno;
779
780                         /* On a RedHat 2.4.2-2  we expect 0, EINVAL
781                          * On a 2.4.x           we expect 0, ENOENT
782                          * On a 2.4.x-ac        we wont get here */
783                         if (err_stat == 0 && err_quota == EINVAL) {
784                                 kernel_qfmt[kernel_qfmt_num++] = QF_VFSV0;
785                                 kernel_iface = IFACE_VFSV0;
786                         }
787                         else {
788                                 kernel_qfmt[kernel_qfmt_num++] = QF_VFSOLD;
789                                 kernel_iface = IFACE_VFSOLD;
790                         }
791                 }
792         }
793         if (sigaction(SIGSEGV, &oldsig, NULL) < 0)
794                 die(2, _("Cannot reset signal handler: %s\n"), strerror(errno));
795 }
796
797 /* Return whether kernel is able to handle given format */
798 int kern_qfmt_supp(int fmt)
799 {
800         int i;
801
802         if (fmt == -1)
803                 return kernel_qfmt_num > 0;
804
805         for (i = 0; i < kernel_qfmt_num; i++)
806                 if (fmt == kernel_qfmt[i])
807                         return 1;
808         return 0;
809 }
810
811 /* Check whether old quota is turned on on given device */
812 static int v1_kern_quota_on(const char *dev, int type)
813 {
814         char tmp[1024];         /* Just temporary buffer */
815         qid_t id = (type == USRQUOTA) ? getuid() : getgid();
816
817         if (!quotactl(QCMD(Q_V1_GETQUOTA, type), dev, id, tmp)) /* OK? */
818                 return 1;
819         return 0;
820 }
821
822 /* Check whether new quota is turned on on given device */
823 static int v2_kern_quota_on(const char *dev, int type)
824 {
825         char tmp[1024];         /* Just temporary buffer */
826         qid_t id = (type == USRQUOTA) ? getuid() : getgid();
827
828         if (!quotactl(QCMD(Q_V2_GETQUOTA, type), dev, id, tmp)) /* OK? */
829                 return 1;
830         return 0;
831 }
832
833 /* Check whether XFS quota is turned on on given device */
834 static int xfs_kern_quota_on(const char *dev, int type)
835 {
836         struct xfs_mem_dqinfo info;
837
838         if (!quotactl(QCMD(Q_XFS_GETQSTAT, type), dev, 0, (void *)&info)) {
839                 if (type == USRQUOTA && (info.qs_flags & XFS_QUOTA_UDQ_ACCT))
840                         return 1;
841                 if (type == GRPQUOTA && (info.qs_flags & XFS_QUOTA_GDQ_ACCT))
842                         return 1;
843         }
844         return 0;
845 }
846
847 /*
848  *      Check whether is quota turned on on given device for given type
849  */
850 int kern_quota_on(const char *dev, int type, int fmt)
851 {
852         /* Check whether quota is turned on... */
853         if (kernel_iface == IFACE_GENERIC) {
854                 int actfmt;
855
856                 if (quotactl(QCMD(Q_GETFMT, type), dev, 0, (void *)&actfmt) < 0)
857                         return -1;
858                 actfmt = kern2utilfmt(actfmt);
859                 if (actfmt < 0)
860                         return -1;
861                 return actfmt;
862         }
863         if ((fmt == -1 || fmt == QF_VFSV0) &&
864             v2_kern_quota_on(dev, type))        /* VFSv0 quota format */
865                 return QF_VFSV0;
866         if ((fmt == -1 || fmt == QF_XFS) &&
867             xfs_kern_quota_on(dev, type))       /* XFS quota format */
868                 return QF_XFS;
869         if ((fmt == -1 || fmt == QF_VFSOLD) &&
870             v1_kern_quota_on(dev, type))        /* Old quota format */
871                 return QF_VFSOLD;
872         return -1;
873 }
874
875 /*
876  *
877  *      mtab/fstab handling routines
878  *
879  */
880
881 struct mount_entry {
882         char *me_type;          /* Type of filesystem for given entry */
883         char *me_opts;          /* Options of filesystem */
884         dev_t me_dev;           /* Device filesystem is mounted on */
885         ino_t me_ino;           /* Inode number of root of filesystem */
886         const char *me_devname; /* Name of device (after pass through get_device_name()) */
887         const char *me_dir;     /* One of mountpoints of filesystem */
888 };
889
890 struct searched_dir {
891         int sd_dir;             /* Is searched dir mountpoint or in fact device? */
892         dev_t sd_dev;           /* Device mountpoint lies on */
893         ino_t sd_ino;           /* Inode number of mountpoint */
894         const char *sd_name;    /* Name of given dir/device */
895 };
896
897 #define ALLOC_ENTRIES_NUM 16    /* Allocate entries by this number */
898 #define AUTOFS_DIR_MAX 64       /* Maximum number of autofs directories */
899
900 static int mnt_entries_cnt;     /* Number of cached mountpoint entries */
901 static struct mount_entry *mnt_entries; /* Cached mounted filesystems */
902 static int check_dirs_cnt, act_checked; /* Number of dirs to check; Actual checked dir/(mountpoint in case of -a) */
903 static struct searched_dir *check_dirs; /* Directories to check */
904
905 /* Cache mtab/fstab */
906 static int cache_mnt_table(int flags)
907 {
908         FILE *mntf;
909         struct mntent *mnt;
910         struct stat st;
911         struct statfs fsstat;
912         int allocated = 0, i = 0;
913         dev_t dev = 0;
914         char mntpointbuf[PATH_MAX];
915         int autofsdircnt = 0;
916         char autofsdir[AUTOFS_DIR_MAX][PATH_MAX];
917
918 #ifdef ALT_MTAB
919         mntf = setmntent(ALT_MTAB, "r");
920         if (mntf)
921                 goto alloc;
922 #endif
923         mntf = setmntent(_PATH_MOUNTED, "r");
924         if (mntf)
925                 goto alloc;
926         /* Fallback to fstab when mtab not available */
927         if (!(mntf = setmntent(_PATH_MNTTAB, "r"))) {
928                 errstr(_("Cannot open any file with mount points.\n"));
929                 return -1;
930         }
931 alloc:
932         mnt_entries = smalloc(sizeof(struct mount_entry) * ALLOC_ENTRIES_NUM);
933         mnt_entries_cnt = 0;
934         allocated += ALLOC_ENTRIES_NUM;
935         while ((mnt = getmntent(mntf))) {
936                 const char *devname;
937
938                 if (!(devname = get_device_name(mnt->mnt_fsname))) {
939                         errstr(_("Cannot get device name for %s\n"), mnt->mnt_fsname);
940                         continue;
941                 }
942
943                 /* Check for mountpoints under autofs and skip them*/
944                 for (i = 0; i < autofsdircnt; i++) {
945                         int slen = strlen(autofsdir[i]);
946
947                         if (slen <= strlen(mnt->mnt_dir) && !strncmp(autofsdir[i], mnt->mnt_dir, slen))
948                                 break;
949                 }
950                 if (i < autofsdircnt) {
951                         free((char *)devname);
952                         continue;
953                 }
954                                 
955                 if (flags & MS_NO_AUTOFS && !strcmp(mnt->mnt_type, MNTTYPE_AUTOFS)) {   /* Autofs dir to remember? */
956                         if (autofsdircnt == AUTOFS_DIR_MAX)
957                                 die(3, "Too many autofs mountpoints. Please contact <jack@suse.cz>\n");
958                         snprintf(autofsdir[autofsdircnt++], PATH_MAX, "%s/", mnt->mnt_dir);
959                         free((char *)devname);
960                         continue;
961                 }
962                 
963                 if (flags & MS_LOCALONLY && nfs_fstype(mnt->mnt_type)) {
964                         free((char *)devname);
965                         continue;
966                 }
967
968                 /* Further we are not interested in mountpoints without quotas and
969                    we don't want to touch them */
970                 if (!hasquota(mnt, USRQUOTA, flags) && !hasquota(mnt, GRPQUOTA, flags)) {
971                         free((char *)devname);
972                         continue;
973                 }
974                         
975                 if (!realpath(mnt->mnt_dir, mntpointbuf)) {
976                         errstr(_("Cannot resolve mountpoint path %s: %s\n"), mnt->mnt_dir, strerror(errno));
977                         free((char *)devname);
978                         continue;
979                 }
980                 
981                 if (statfs(mntpointbuf, &fsstat) != 0) {
982                         errstr(_("Cannot statfs() %s: %s\n"), mntpointbuf, strerror(errno));
983                         free((char *)devname);
984                         continue;
985                 }
986                 /* Do not scan quotas on "magic" automount points */
987                 if (fsstat.f_blocks == 0 && fsstat.f_bfree == 0 && fsstat.f_bavail == 0) {
988                         free((char *)devname);
989                         continue;
990                 }
991
992                 if (!nfs_fstype(mnt->mnt_type)) {
993                         if (stat(devname, &st) < 0) {   /* Can't stat mounted device? */
994                                 errstr(_("Cannot stat() mounted device %s: %s\n"), devname, strerror(errno));
995                                 free((char *)devname);
996                                 continue;
997                         }
998                         if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode) && !S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) {
999                                 unsupporteddev:
1000                                 errstr(_("Device (%s) filesystem is mounted on unsupported device type. Skipping.\n"), devname);
1001                                 free((char *)devname);
1002                                 continue;
1003                         } else {
1004                                 char *opt;
1005                                 
1006                                 if (hasmntopt(mnt, MNTOPT_BIND)) {
1007                                         free((char *)devname);
1008                                         continue;       /* We just ignore bind mounts... */
1009                                 }
1010                                 else if ((opt = hasmntopt(mnt, MNTOPT_LOOP))) {
1011                                         char loopdev[PATH_MAX];
1012                                         int i;
1013
1014                                         if (!(opt = strchr(opt, '='))) {
1015                                                 errstr(_("Cannot find device of loopback mount in options for %s. Skipping.\n"), devname);
1016                                                 free((char *)devname);
1017                                                 continue;
1018                                         }
1019                                         /* Copy the device name */
1020                                         for (opt++, i = 0; *opt && i < sizeof(loopdev)-1 && *opt != ','; opt++, i++)
1021                                                 loopdev[i] = *opt;
1022                                         loopdev[i] = 0;
1023                                         if (stat(loopdev, &st) < 0) {   /* Can't stat loopback device? */
1024                                                 errstr(_("Cannot stat() loopback device %s: %s\n"), opt, strerror(errno));
1025                                                 free((char *)devname);
1026                                                 continue;
1027                                         }
1028                                         if (!S_ISBLK(st.st_mode)) {
1029                                                 errstr(_("Loopback device %s is not block device!\n"), opt);
1030                                                 free((char *)devname);
1031                                                 continue;
1032                                         }
1033                                         dev = st.st_rdev;
1034                                         free((char *)devname);
1035                                         devname = sstrdup(loopdev);
1036                                 } else {
1037                                         if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
1038                                                 goto unsupporteddev;
1039                                         dev = st.st_rdev;
1040                                 }
1041                         }
1042                         for (i = 0; i < mnt_entries_cnt && mnt_entries[i].me_dev != dev; i++);
1043                 }
1044                 /* Cope with network filesystems or new mountpoint */
1045                 if (nfs_fstype(mnt->mnt_type) || i == mnt_entries_cnt) {
1046                         if (stat(mnt->mnt_dir, &st) < 0) {      /* Can't stat mountpoint? We have better ignore it... */
1047                                 errstr(_("Cannot stat() mountpoint %s: %s\n"), mnt->mnt_dir, strerror(errno));
1048                                 free((char *)devname);
1049                                 continue;
1050                         }
1051                         if (nfs_fstype(mnt->mnt_type)) {
1052                                 /* For network filesystems we must get device from root */
1053                                 dev = st.st_dev;
1054                                 if (!(flags & MS_NFS_ALL)) {
1055                                         for (i = 0; i < mnt_entries_cnt && mnt_entries[i].me_dev != dev; i++);
1056                                 }
1057                                 else    /* Always behave as if the device was unique */
1058                                         i = mnt_entries_cnt;
1059                         }
1060                 }
1061                 if (i == mnt_entries_cnt) {     /* New mounted device? */
1062                         if (allocated == mnt_entries_cnt) {
1063                                 allocated += ALLOC_ENTRIES_NUM;
1064                                 mnt_entries = srealloc(mnt_entries, allocated * sizeof(struct mount_entry));
1065                         }
1066                         mnt_entries[i].me_type = sstrdup(mnt->mnt_type);
1067                         mnt_entries[i].me_opts = sstrdup(mnt->mnt_opts);
1068                         mnt_entries[i].me_dev = dev;
1069                         mnt_entries[i].me_ino = st.st_ino;
1070                         mnt_entries[i].me_devname = devname;
1071                         mnt_entries[i].me_dir = sstrdup(mntpointbuf);
1072                         mnt_entries_cnt++;
1073                 }
1074                 else 
1075                         free((char *)devname);  /* We don't need it any more */
1076         }
1077         endmntent(mntf);
1078
1079         return 0;
1080 }
1081
1082 /* Find mountpoint of filesystem hosting dir in 'st'; Store it in 'st' */
1083 static const char *find_dir_mntpoint(struct stat *st)
1084 {
1085         int i;
1086
1087         for (i = 0; i < mnt_entries_cnt; i++)
1088                 if (mnt_entries[i].me_dev == st->st_dev) {
1089                         st->st_ino = mnt_entries[i].me_ino;
1090                         return mnt_entries[i].me_dir;
1091                 }
1092         return NULL;
1093 }
1094
1095 /* Process and store given paths */
1096 static int process_dirs(int dcnt, char **dirs, int flags)
1097 {
1098         struct stat st;
1099         int i;
1100         char mntpointbuf[PATH_MAX];
1101
1102         check_dirs_cnt = 0;
1103         act_checked = -1;
1104         if (dcnt) {
1105                 check_dirs = smalloc(sizeof(struct searched_dir) * dcnt);
1106                 for (i = 0; i < dcnt; i++) {
1107                         if (!strncmp(dirs[i], "UUID=", 5) || !strncmp(dirs[i], "LABEL=", 6)) {
1108                                 char *devname = (char *)get_device_name(dirs[i]);
1109
1110                                 if (!devname) {
1111                                         errstr(_("Cannot find a device with %s.\nSkipping...\n"), dirs[i]);
1112                                         continue;
1113                                 }
1114                                 if (stat(devname, &st) < 0) {
1115                                         errstr(_("Cannot stat() a mountpoint with %s: %s\nSkipping...\n"), dirs[i], strerror(errno));
1116                                         free(devname);
1117                                         continue;
1118                                 }
1119                                 free(devname);
1120                         }
1121                         else
1122                                 if (stat(dirs[i], &st) < 0) {
1123                                         errstr(_("Cannot stat() given mountpoint %s: %s\nSkipping...\n"), dirs[i], strerror(errno));
1124                                         continue;
1125                                 }
1126                         check_dirs[check_dirs_cnt].sd_dir = S_ISDIR(st.st_mode);
1127                         if (S_ISDIR(st.st_mode)) {
1128                                 const char *realmnt = dirs[i];
1129
1130                                 /* Return st of mountpoint of dir in st.. */
1131                                 if (flags & MS_NO_MNTPOINT && !(realmnt = find_dir_mntpoint(&st))) {
1132                                         if (!(flags & MS_QUIET))
1133                                                 errstr(_("Cannot find a filesystem mountpoint for directory %s\n"), dirs[i]);
1134                                         continue;
1135                                 }
1136                                 check_dirs[check_dirs_cnt].sd_dev = st.st_dev;
1137                                 check_dirs[check_dirs_cnt].sd_ino = st.st_ino;
1138                                 if (!realpath(realmnt, mntpointbuf)) {
1139                                         errstr(_("Cannot resolve path %s: %s\n"), realmnt, strerror(errno));
1140                                         continue;
1141                                 }
1142                         }
1143                         else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) {
1144                                 int mentry;
1145
1146                                 check_dirs[check_dirs_cnt].sd_dev = st.st_rdev;
1147                                 for (mentry = 0; mentry < mnt_entries_cnt && mnt_entries[mentry].me_dev != st.st_rdev; mentry++);
1148                                 if (mentry == mnt_entries_cnt) {
1149                                         if (!(flags & MS_QUIET))
1150                                                 errstr(_("Cannot find mountpoint for device %s\n"), dirs[i]);
1151                                         continue;
1152                                 }
1153                                 sstrncpy(mntpointbuf, mnt_entries[mentry].me_dir, PATH_MAX-1);
1154                         }
1155                         else {
1156                                 errstr(_("Specified path %s is not directory nor device.\n"), dirs[i]);
1157                                 continue;
1158                         }
1159                         check_dirs[check_dirs_cnt].sd_name = sstrdup(mntpointbuf);
1160                         check_dirs_cnt++;
1161                 }
1162                 if (!check_dirs_cnt) {
1163                         if (!(flags & MS_QUIET))
1164                                 errstr(_("No correct mountpoint specified.\n"));
1165                         free(check_dirs);
1166                         return -1;
1167                 }
1168         }
1169         return 0;
1170 }
1171
1172 /*
1173  *      Initialize mountpoint scan
1174  */ 
1175 int init_mounts_scan(int dcnt, char **dirs, int flags)
1176 {
1177         if (cache_mnt_table(flags) < 0)
1178                 return -1;
1179         if (process_dirs(dcnt, dirs, flags) < 0) {
1180                 end_mounts_scan();
1181                 return -1;
1182         }
1183         return 0;
1184 }
1185
1186 /* Find next usable mountpoint when scanning all mountpoints */
1187 static int find_next_entry_all(int *pos)
1188 {
1189         struct mntent mnt;
1190
1191         while (++act_checked < mnt_entries_cnt) {
1192                 mnt.mnt_fsname = (char *)mnt_entries[act_checked].me_devname;
1193                 mnt.mnt_type = mnt_entries[act_checked].me_type;
1194                 mnt.mnt_opts = mnt_entries[act_checked].me_opts;
1195                 mnt.mnt_dir = (char *)mnt_entries[act_checked].me_dir;
1196                 if (!hasmntopt(&mnt, MNTOPT_NOAUTO))
1197                         break;
1198         }
1199         if (act_checked >= mnt_entries_cnt)
1200                 return 0;
1201         *pos = act_checked;
1202         return 1;
1203 }
1204
1205 /* Find next usable mountpoint when scanning selected mountpoints */
1206 static int find_next_entry_sel(int *pos)
1207 {
1208         int i;
1209         struct searched_dir *sd;
1210
1211 restart:
1212         if (++act_checked == check_dirs_cnt)
1213                 return 0;
1214         sd = check_dirs + act_checked;
1215         for (i = 0; i < mnt_entries_cnt; i++) {
1216                 if (sd->sd_dir) {
1217                         if (sd->sd_dev == mnt_entries[i].me_dev && sd->sd_ino == mnt_entries[i].me_ino)
1218                                 break;
1219                 }
1220                 else
1221                         if (sd->sd_dev == mnt_entries[i].me_dev)
1222                                 break;
1223         }
1224         if (i == mnt_entries_cnt) {
1225                 errstr(_("Mountpoint (or device) %s not found or has no quota enabled.\n"), sd->sd_name);
1226                 goto restart;
1227         }
1228         *pos = i;
1229         return 1;
1230 }
1231
1232 /*
1233  *      Return next directory from the list
1234  */
1235 struct mntent *get_next_mount(void)
1236 {
1237         static struct mntent mnt;
1238         int mntpos;
1239
1240         if (!check_dirs_cnt) {  /* Scan all mountpoints? */
1241                 if (!find_next_entry_all(&mntpos))
1242                         return NULL;
1243                 mnt.mnt_dir = (char *)mnt_entries[mntpos].me_dir;
1244         }
1245         else {
1246                 if (!find_next_entry_sel(&mntpos))
1247                         return NULL;
1248                 mnt.mnt_dir = (char *)check_dirs[act_checked].sd_name;
1249         }
1250         mnt.mnt_fsname = (char *)mnt_entries[mntpos].me_devname;
1251         mnt.mnt_type = mnt_entries[mntpos].me_type;
1252         mnt.mnt_opts = mnt_entries[mntpos].me_opts;
1253         return &mnt;
1254 }
1255
1256 /*
1257  *      Free all structures allocated for mountpoint scan
1258  */
1259 void end_mounts_scan(void)
1260 {
1261         int i;
1262
1263         for (i = 0; i < mnt_entries_cnt; i++) {
1264                 free(mnt_entries[i].me_type);
1265                 free(mnt_entries[i].me_opts);
1266                 free((char *)mnt_entries[i].me_devname);
1267                 free((char *)mnt_entries[i].me_dir);
1268         }
1269         free(mnt_entries);
1270         mnt_entries = NULL;
1271         mnt_entries_cnt = 0;
1272         if (check_dirs_cnt) {
1273                 for (i = 0; i < check_dirs_cnt; i++)
1274                         free((char *)check_dirs[i].sd_name);
1275                 free(check_dirs);
1276         }
1277         check_dirs = NULL;
1278         check_dirs_cnt = 0;
1279 }