1 /**
2 * collectd - src/quota_mnt.c
3 * Copyright (C) 2005 Niki W. Waibel
4 *
5 * This program is free software; you can redistribute it and/
6 * or modify it under the terms of the GNU General Public Li-
7 * cence as published by the Free Software Foundation; either
8 * version 2 of the Licence, or any later version.
9 *
10 * This program is distributed in the hope that it will be use-
11 * ful, but WITHOUT ANY WARRANTY; without even the implied war-
12 * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public Licence for more details.
14 *
15 * You should have received a copy of the GNU General Public
16 * Licence along with this program; if not, write to the Free
17 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
18 * USA.
19 *
20 * Author:
21 * Niki W. Waibel <niki.waibel@gmx.net>
22 **/
24 #include "common.h"
25 #include "quota_debug.h"
26 #include "quota_common.h"
27 #include "quota_fs.h"
28 #include "quota_mnt.h"
31 /* if sthg is changed here also change it in configure.in!!! */
32 #if HAVE_MNTENT_H /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
33 # include <mntent.h>
34 #endif
35 #if HAVE_MNTTAB_H /* SVR2, SVR3. */
36 # include <mnttab.h>
37 #endif
38 #if HAVE_PATHS_H
39 # include <paths.h>
40 #endif
41 #if HAVE_SYS_FS_TYPES_H /* Ultrix. */
42 # include <sys/fs_types.h>
43 #endif
44 #if HAVE_SYS_MNTENT_H
45 # include <sys/mntent.h>
46 #endif
47 #if HAVE_SYS_MNTTAB_H /* SVR4. */
48 # include <sys/mnttab.h>
49 #endif
50 #if HAVE_SYS_MOUNT_H /* 4.4BSD, Ultrix. */
51 # include <sys/mount.h>
52 #endif
53 #if HAVE_SYS_VFSTAB_H
54 # include <sys/vfstab.h>
55 #endif
56 #if HAVE_SYS_QUOTA_H
57 # include <sys/quota.h>
58 #endif
59 #if HAVE_SYS_VFS_H
60 # include <sys/vfs.h>
61 #endif
62 /* END if sthg is changed here also change it in configure.in!!! */
63 #if HAVE_XFS_XQM_H
64 # include <xfs/xqm.h>
65 #define xfs_mem_dqinfo fs_quota_stat
66 #define Q_XFS_GETQSTAT Q_XGETQSTAT
67 #define XFS_SUPER_MAGIC_STR "XFSB"
68 #define XFS_SUPER_MAGIC2_STR "BSFX"
69 #endif
71 #include "quota_mntopt.h"
75 /* *** *** *** local functions *** *** *** */
79 /* stolen from quota-3.13 (quota-tools) */
81 #define PROC_PARTITIONS "/proc/partitions"
82 #define DEVLABELDIR "/dev"
83 #define UUID 1
84 #define VOL 2
86 #define AUTOFS_DIR_MAX 64 /* Maximum number of autofs directories */
88 static struct uuidCache_s {
89 struct uuidCache_s *next;
90 char uuid[16];
91 char *label;
92 char *device;
93 } *uuidCache = NULL;
95 #define EXT2_SUPER_MAGIC 0xEF53
96 struct ext2_super_block {
97 unsigned char s_dummy1[56];
98 unsigned char s_magic[2];
99 unsigned char s_dummy2[46];
100 unsigned char s_uuid[16];
101 char s_volume_name[16];
102 };
103 #define ext2magic(s) ((unsigned int)s.s_magic[0] \
104 + (((unsigned int)s.s_magic[1]) << 8))
106 #if HAVE_XFS_XQM_H
107 struct xfs_super_block {
108 unsigned char s_magic[4];
109 unsigned char s_dummy[28];
110 unsigned char s_uuid[16];
111 unsigned char s_dummy2[60];
112 char s_fsname[12];
113 };
114 #endif /* HAVE_XFS_XQM_H */
116 #define REISER_SUPER_MAGIC "ReIsEr2Fs"
117 struct reiserfs_super_block {
118 unsigned char s_dummy1[52];
119 unsigned char s_magic[10];
120 unsigned char s_dummy2[22];
121 unsigned char s_uuid[16];
122 char s_volume_name[16];
123 };
125 /* for now, only ext2 and xfs are supported */
126 static int
127 get_label_uuid(const char *device, char **label, char *uuid)
128 {
129 /* start with ext2 and xfs tests, taken from mount_guess_fstype */
130 /* should merge these later */
131 int fd, rv = 1;
132 size_t namesize;
133 struct ext2_super_block e2sb;
134 #if HAVE_XFS_XQM_H
135 struct xfs_super_block xfsb;
136 #endif
137 struct reiserfs_super_block reisersb;
139 fd = open(device, O_RDONLY);
140 if(fd == -1) {
141 return rv;
142 }
144 if(lseek(fd, 1024, SEEK_SET) == 1024
145 && read(fd, (char *)&e2sb, sizeof(e2sb)) == sizeof(e2sb)
146 && ext2magic(e2sb) == EXT2_SUPER_MAGIC) {
147 memcpy(uuid, e2sb.s_uuid, sizeof(e2sb.s_uuid));
148 namesize = sizeof(e2sb.s_volume_name);
149 *label = smalloc(namesize + 1);
150 sstrncpy(*label, e2sb.s_volume_name, namesize);
151 rv = 0;
152 #if HAVE_XFS_XQM_H
153 } else if(lseek(fd, 0, SEEK_SET) == 0
154 && read(fd, (char *)&xfsb, sizeof(xfsb)) == sizeof(xfsb)
155 && (strncmp((char *)&xfsb.s_magic, XFS_SUPER_MAGIC_STR, 4) == 0 ||
156 strncmp((char *)&xfsb.s_magic, XFS_SUPER_MAGIC2_STR, 4) == 0)) {
157 memcpy(uuid, xfsb.s_uuid, sizeof(xfsb.s_uuid));
158 namesize = sizeof(xfsb.s_fsname);
159 *label = smalloc(namesize + 1);
160 sstrncpy(*label, xfsb.s_fsname, namesize);
161 rv = 0;
162 #endif /* HAVE_XFS_XQM_H */
163 } else if(lseek(fd, 65536, SEEK_SET) == 65536
164 && read(fd, (char *)&reisersb, sizeof(reisersb)) == sizeof(reisersb)
165 && !strncmp((char *)&reisersb.s_magic, REISER_SUPER_MAGIC, 9)) {
166 memcpy(uuid, reisersb.s_uuid, sizeof(reisersb.s_uuid));
167 namesize = sizeof(reisersb.s_volume_name);
168 *label = smalloc(namesize + 1);
169 sstrncpy(*label, reisersb.s_volume_name, namesize);
170 rv = 0;
171 }
172 close(fd);
173 return rv;
174 }
176 static void
177 uuidcache_addentry(char *device, char *label, char *uuid)
178 {
179 struct uuidCache_s *last;
181 if(!uuidCache) {
182 last = uuidCache = smalloc(sizeof(*uuidCache));
183 } else {
184 for(last = uuidCache; last->next; last = last->next);
185 last->next = smalloc(sizeof(*uuidCache));
186 last = last->next;
187 }
188 last->next = NULL;
189 last->device = device;
190 last->label = label;
191 memcpy(last->uuid, uuid, sizeof(last->uuid));
192 }
194 static void
195 uuidcache_init(void)
196 {
197 char line[100];
198 char *s;
199 int ma, mi, sz;
200 static char ptname[100];
201 FILE *procpt;
202 char uuid[16], *label = NULL;
203 char device[110];
204 int firstPass;
205 int handleOnFirst;
207 if(uuidCache) {
208 return;
209 }
211 procpt = fopen(PROC_PARTITIONS, "r");
212 if(procpt == NULL) {
213 return;
214 }
216 for(firstPass = 1; firstPass >= 0; firstPass--) {
217 fseek(procpt, 0, SEEK_SET);
218 while(fgets(line, sizeof(line), procpt)) {
219 if(sscanf(line, " %d %d %d %[^\n ]",
220 &ma, &mi, &sz, ptname) != 4)
221 {
222 continue;
223 }
225 /* skip extended partitions (heuristic: size 1) */
226 if(sz == 1) {
227 continue;
228 }
230 /* look only at md devices on first pass */
231 handleOnFirst = !strncmp(ptname, "md", 2);
232 if(firstPass != handleOnFirst) {
233 continue;
234 }
236 /* skip entire disk (minor 0, 64, ... on ide;
237 0, 16, ... on sd) */
238 /* heuristic: partition name ends in a digit */
240 for(s = ptname; *s; s++);
242 if(isdigit((int)s[-1])) {
243 /*
244 * Note: this is a heuristic only - there is no reason
245 * why these devices should live in /dev.
246 * Perhaps this directory should be specifiable by option.
247 * One might for example have /devlabel with links to /dev
248 * for the devices that may be accessed in this way.
249 * (This is useful, if the cdrom on /dev/hdc must not
250 * be accessed.)
251 */
252 snprintf(device, sizeof(device), "%s/%s",
253 DEVLABELDIR, ptname);
254 if(!get_label_uuid(device, &label, uuid)) {
255 uuidcache_addentry(sstrdup(device),
256 label, uuid);
257 }
258 }
259 }
260 }
261 fclose(procpt);
262 }
264 static unsigned char
265 fromhex(char c)
266 {
267 if(isdigit((int)c)) {
268 return (c - '0');
269 } else if(islower((int)c)) {
270 return (c - 'a' + 10);
271 } else {
272 return (c - 'A' + 10);
273 }
274 }
276 static char *
277 get_spec_by_x(int n, const char *t)
278 {
279 struct uuidCache_s *uc;
281 uuidcache_init();
282 uc = uuidCache;
284 while(uc) {
285 switch(n) {
286 case UUID:
287 if(!memcmp(t, uc->uuid, sizeof(uc->uuid))) {
288 return sstrdup(uc->device);
289 }
290 break;
291 case VOL:
292 if(!strcmp(t, uc->label)) {
293 return sstrdup(uc->device);
294 }
295 break;
296 }
297 uc = uc->next;
298 }
299 return NULL;
300 }
302 static char *
303 get_spec_by_uuid(const char *s)
304 {
305 char uuid[16];
306 int i;
308 if(strlen(s) != 36
309 || s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-') {
310 goto bad_uuid;
311 }
313 for(i=0; i<16; i++) {
314 if(*s == '-') {
315 s++;
316 }
317 if(!isxdigit((int)s[0]) || !isxdigit((int)s[1])) {
318 goto bad_uuid;
319 }
320 uuid[i] = ((fromhex(s[0]) << 4) | fromhex(s[1]));
321 s += 2;
322 }
323 return get_spec_by_x(UUID, uuid);
325 bad_uuid:
326 DBG("Found an invalid UUID: %s", s);
327 return NULL;
328 }
330 static char *
331 get_spec_by_volume_label(const char *s)
332 {
333 return get_spec_by_x(VOL, s);
334 }
336 static char *
337 get_device_name(const char *item)
338 {
339 char *rc;
341 if(!strncmp(item, "UUID=", 5)) {
342 DBG("TODO: check UUID= code!");
343 rc = get_spec_by_uuid(item + 5);
344 } else if(!strncmp(item, "LABEL=", 6)) {
345 DBG("TODO: check LABEL= code!");
346 rc = get_spec_by_volume_label(item + 6);
347 } else {
348 rc = sstrdup(item);
349 }
350 if(!rc) {
351 DBG("Error checking device name: %s", item);
352 }
353 return rc;
354 }
356 /*
357 * Check for various kinds of NFS filesystem
358 */
359 int
360 nfs_fstype(char *type)
361 {
362 return !strcmp(type, MNTTYPE_NFS) || !strcmp(type, MNTTYPE_NFS4);
363 }
365 #if HAVE_XFS_XQM_H
366 /*
367 * Check for XFS filesystem with quota accounting enabled
368 */
369 static int
370 hasxfsquota(struct mntent *mnt, int type)
371 {
372 int ret = 0;
373 u_int16_t sbflags;
374 struct xfs_mem_dqinfo info;
375 const char *dev = get_device_name(mnt->mnt_fsname);
377 if(!dev) {
378 return ret;
379 }
381 memset(&info, 0, sizeof(struct xfs_mem_dqinfo));
382 if(!quotactl(QCMD(Q_XFS_GETQSTAT, type), dev, 0, (void *)&info)) {
383 sbflags = (info.qs_flags & 0xff00) >> 8;
384 if(type == USRQUOTA && (info.qs_flags & XFS_QUOTA_UDQ_ACCT)) {
385 ret = 1;
386 } else if(type == GRPQUOTA && (info.qs_flags & XFS_QUOTA_GDQ_ACCT)) {
387 ret = 1;
388 }
389 #ifdef XFS_ROOTHACK
390 /*
391 * Old XFS filesystems (up to XFS 1.2 / Linux 2.5.47) had a
392 * hack to allow enabling quota on the root filesystem without
393 * having to specify it at mount time.
394 */
395 else if(strcmp(mnt->mnt_dir, "/")) {
396 ret = 0;
397 } else if(type == USRQUOTA && (sbflags & XFS_QUOTA_UDQ_ACCT)) {
398 ret = 1;
399 } else if(type == GRPQUOTA && (sbflags & XFS_QUOTA_GDQ_ACCT)) {
400 ret = 1;
401 #endif /* XFS_ROOTHACK */
402 }
403 sfree((char *)dev);
404 return ret;
405 }
406 #endif /* HAVE_XFS_XQM_H */
409 #if HAVE_LISTMNTENT
410 static void
411 quota_mnt_listmntent(struct tabmntent *mntlist, quota_mnt_t **list)
412 {
413 struct *p;
414 struct mntent *mnt;
416 for(p = mntlist; p; p = p->next) {
417 mnt = p->ment;
418 *list = smalloc(sizeof(quota_mnt_t));
419 list->device = strdup(mnt->mnt_fsname);
420 list->name = strdup(mnt->mnt_dir);
421 list->type = strdup(mnt->mnt_type);
422 list->next = NULL;
423 list = &(ist->next);
424 }
425 freemntlist(mntlist);
426 }
427 #endif /* HAVE_LISTMNTENT */
431 #if HAVE_GETVFSENT
432 static void
433 quota_mnt_getvfsmnt(FILE *mntf, quota_mnt_t **list)
434 {
435 DBG("TODO: getvfsmnt");
436 *list = NULL;
437 }
438 #endif /* HAVE_GETVFSENT */
440 char *
441 quota_mnt_checkmountopt(char *line, char *keyword, int full)
442 {
443 char *line2, *l2;
444 int l = strlen(keyword);
445 char *p1, *p2;
447 if(line == NULL || keyword == NULL) {
448 return NULL;
449 }
450 if(full != 0) {
451 full = 1;
452 }
454 line2 = sstrdup(line);
455 l2 = line2;
456 while(*l2 != '\0') {
457 if(*l2 == ',') {
458 *l2 = '\0';
459 }
460 l2++;
461 }
463 p1 = line - 1;
464 p2 = strchr(line, ',');
465 do {
466 if(strncmp(line2+(p1-line)+1, keyword, l+full) == 0) {
467 free(line2);
468 return p1+1;
469 }
470 p1 = p2;
471 if(p1 != NULL) {
472 p2 = strchr(p1+1, ',');
473 }
474 } while(p1 != NULL);
476 free(line2);
477 return NULL;
478 } /* char *quota_mnt_checkmountopt(char *line, char *keyword, int full) */
480 char *
481 quota_mnt_getmountopt(char *line, char *keyword)
482 {
483 char *r;
485 r = quota_mnt_checkmountopt(line, keyword, 0);
486 if(r != NULL) {
487 char *p;
488 r += strlen(keyword);
489 p = strchr(r, ',');
490 if(p == NULL) {
491 if(strlen(r) == 0) {
492 return NULL;
493 }
494 return sstrdup(r);
495 } else {
496 char *m;
497 if((p-r) == 1) {
498 return NULL;
499 }
500 m = (char *)smalloc(p-r+1);
501 sstrncpy(m, r, p-r+1);
502 return m;
503 }
504 }
505 return r;
506 } /* char *quota_mnt_getmountopt(char *line, char *keyword) */
508 #if HAVE_GETMNTENT
509 static void
510 quota_mnt_getmntent(FILE *mntf, quota_mnt_t **list)
511 {
512 struct mntent *mnt;
514 while((mnt = getmntent(mntf)) != NULL) {
515 char *loop = NULL, *device = NULL;
516 char *usrjquota = NULL;
517 char *grpjquota = NULL;
518 char *jqfmt = NULL;
520 #if 0
521 DBG("------------------");
522 DBG("mnt->mnt_fsname %s", mnt->mnt_fsname);
523 DBG("mnt->mnt_dir %s", mnt->mnt_dir);
524 DBG("mnt->mnt_type %s", mnt->mnt_type);
525 DBG("mnt->mnt_opts %s", mnt->mnt_opts);
526 DBG("mnt->mnt_freq %d", mnt->mnt_freq);
527 DBG("mnt->mnt_passno %d", mnt->mnt_passno);
528 #endif
530 if(quota_fs_issupported(mnt->mnt_type) == EXIT_FAILURE)
531 {
532 DBG("unsupportet fs (%s) %s (%s): ignored",
533 mnt->mnt_type, mnt->mnt_dir, mnt->mnt_fsname);
534 continue;
535 }
537 if(quota_mnt_checkmountopt(mnt->mnt_opts, MNTOPT_NOQUOTA, 1) != NULL) {
538 DBG("noquota option on fs (%s) %s (%s): ignored",
539 mnt->mnt_type, mnt->mnt_dir, mnt->mnt_fsname);
540 continue;
541 }
543 if(quota_mnt_checkmountopt(mnt->mnt_opts, "bind", 1) != NULL) {
544 DBG("bind mount on fs (%s) %s (%s): ignored",
545 mnt->mnt_type, mnt->mnt_dir, mnt->mnt_fsname);
546 continue;
547 }
549 loop = quota_mnt_getmountopt(mnt->mnt_opts, "loop=");
550 if(loop == NULL) { /* no loop= mount */
551 device = get_device_name(mnt->mnt_fsname);
552 if(device == NULL) {
553 DBG("can't get devicename for fs (%s) %s (%s)"
554 ": ignored", mnt->mnt_type,
555 mnt->mnt_dir, mnt->mnt_fsname);
556 continue;
557 }
558 } else {
559 device = loop;
560 }
562 usrjquota = quota_mnt_getmountopt(mnt->mnt_opts, "usrjquota=");
563 grpjquota = quota_mnt_getmountopt(mnt->mnt_opts, "grpjquota=");
564 jqfmt = quota_mnt_getmountopt(mnt->mnt_opts, "jqfmt=");
566 #if HAVE_XFS_XQM_H
567 if(!strcmp(mnt->mnt_type, MNTTYPE_XFS)) {
568 if(hasxfsquota(mnt, USRQUOTA) == 0
569 && hasxfsquota(mnt, GRPQUOTA) == 0)
570 {
571 DBG("no quota on fs (%s) %s (%s): ignored",
572 mnt->mnt_type, mnt->mnt_dir,
573 mnt->mnt_fsname);
574 sfree(loop);
575 sfree(usrjquota);
576 sfree(grpjquota);
577 sfree(jqfmt);
578 continue;
579 }
580 } else {
581 #endif /* HAVE_XFS_XQM_H */
582 if(quota_mnt_checkmountopt(mnt->mnt_opts, MNTOPT_QUOTA, 1)
583 == NULL
584 && quota_mnt_checkmountopt(mnt->mnt_opts, MNTOPT_USRQUOTA, 1)
585 == NULL
586 && quota_mnt_checkmountopt(mnt->mnt_opts, MNTOPT_GRPQUOTA, 1)
587 == NULL
588 && quota_fs_isnfs(mnt->mnt_type) == EXIT_FAILURE)
589 {
590 DBG("neither quota/usrquota/grpquota option"
591 " nor nfs fs (%s) %s (%s): ignored",
592 mnt->mnt_type, mnt->mnt_dir, mnt->mnt_fsname);
593 sfree(loop);
594 sfree(usrjquota);
595 sfree(grpjquota);
596 sfree(jqfmt);
597 continue;
598 }
599 #if HAVE_XFS_XQM_H
600 }
601 #endif /* HAVE_XFS_XQM_H */
602 #if 0
603 DBG("------------------ OK");
604 #endif
605 *list = (quota_mnt_t *)smalloc(sizeof(quota_mnt_t));
606 (*list)->dir = sstrdup(mnt->mnt_dir);
607 (*list)->device = device;
608 (*list)->usrjquota = usrjquota;
609 (*list)->grpjquota = grpjquota;
610 (*list)->jqfmt = jqfmt;
611 (*list)->type = sstrdup(mnt->mnt_type);
612 (*list)->opts = QMO_NONE;
613 /* TODO: this is not sufficient for XFS! */
614 /* TODO: maybe we should anyway NOT rely on the option in the mountfile...
615 ... maybe the fs should be asked direktly all time! */
616 if(quota_mnt_checkmountopt(mnt->mnt_opts, MNTOPT_QUOTA, 1) != NULL
617 || quota_mnt_checkmountopt(mnt->mnt_opts, MNTOPT_USRQUOTA, 1) != NULL) {
618 (*list)->opts |= QMO_USRQUOTA;
619 }
620 if(quota_mnt_checkmountopt(mnt->mnt_opts, MNTOPT_GRPQUOTA, 1) != NULL) {
621 (*list)->opts |= QMO_GRPQUOTA;
622 }
623 (*list)->next = NULL;
624 list = &((*list)->next);
625 } /* while((mnt = getmntent(mntf)) != NULL) */
626 }
627 #endif /* HAVE_GETMNTENT */
631 quota_mnt_t *
632 quota_mnt_getlist(quota_mnt_t **list)
633 {
634 /* yes, i know that the indentation is wrong.
635 but show me a better way to do this... */
636 /* see lib/mountlist.c of coreutils for all
637 gory details! */
638 #if HAVE_GETMNTENT && defined(_PATH_MOUNTED)
639 {
640 FILE *mntf = NULL;
641 if((mntf = setmntent(_PATH_MOUNTED, "r")) == NULL) {
642 DBG("opening %s failed: %s", _PATH_MOUNTED, strerror(errno));
643 #endif
644 #if HAVE_GETMNTENT && defined(MNT_MNTTAB)
645 {
646 FILE *mntf = NULL;
647 if((mntf = setmntent(MNT_MNTTAB, "r")) == NULL) {
648 DBG("opening %s failed: %s", MNT_MNTTAB, strerror(errno));
649 #endif
650 #if HAVE_GETMNTENT && defined(MNTTABNAME)
651 {
652 FILE *mntf = NULL;
653 if((mntf = setmntent(MNTTABNAME, "r")) == NULL) {
654 DBG("opening %s failed: %s", MNTTABNAME, strerror(errno));
655 #endif
656 #if HAVE_GETMNTENT && defined(_PATH_MNTTAB)
657 {
658 FILE *mntf = NULL;
659 if((mntf = setmntent(_PATH_MNTTAB, "r")) == NULL) {
660 DBG("opening %s failed: %s", _PATH_MNTTAB, strerror(errno));
661 #endif
662 #if HAVE_GETVFSENT && defined(VFSTAB)
663 {
664 FILE *mntf = NULL;
665 if((mntf = fopen(VFSTAB, "r")) == NULL) {
666 DBG("opening %s failed: %s", VFSTAB, strerror(errno));
667 #endif
668 #if HAVE_LISTMNTENT
669 {
670 struct tabmntent *mntlist;
672 if(listmntent(&mntlist, KMTAB, NULL, NULL) < 0) {
673 DBG("calling listmntent() failed: %s", strerror(errno));
674 #endif
675 /* give up */
676 DBG("failed get local mountpoints");
677 *list = NULL;
678 return(NULL);
680 #if HAVE_LISTMNTENT
681 } else { quota_mnt_listmntent(mntlist, list); }
682 freemntlist(mntlist);
683 }
684 #endif
685 #if HAVE_GETVFSENT && defined(VFSTAB)
686 } else { quota_mnt_getvfsmnt(mntf, list); }
687 (void)fclose(mntf);
688 }
689 #endif
690 #if HAVE_GETMNTENT && defined(_PATH_MNTTAB)
691 } else { quota_mnt_getmntent(mntf, list); }
692 (void)endmntent(mntf);
693 }
694 #endif
695 #if HAVE_GETMNTENT && defined(MNTTABNAME)
696 } else { quota_mnt_getmntent(mntf, list); }
697 (void)endmntent(mntf);
698 }
699 #endif
700 #if HAVE_GETMNTENT && defined(MNT_MNTTAB)
701 } else { quota_mnt_getmntent(mntf, list); }
702 (void)endmntent(mntf);
703 }
704 #endif
705 #if HAVE_GETMNTENT && defined(_PATH_MOUNTED)
706 } else { quota_mnt_getmntent(mntf, list); }
707 (void)endmntent(mntf);
708 }
709 #endif
710 return(*list);
711 }
713 void
714 quota_mnt_freelist(quota_mnt_t *list)
715 {
716 quota_mnt_t *l = list, *p = NULL;
718 while(l != NULL) {
719 while(l->next != NULL) {
720 p = l;
721 l = l->next;
722 }
723 if(p != NULL) {
724 p->next = NULL;
725 }
726 sfree(l->dir);
727 sfree(l->device);
728 sfree(l->type);
729 sfree(l->usrjquota);
730 sfree(l->grpjquota);
731 sfree(l->jqfmt);
732 sfree(l);
733 p = NULL;
734 if(l != list) {
735 l = list;
736 } else {
737 l = NULL;
738 }
739 } /* while(l != NULL) */
740 } /* void quota_mnt_freelist(quota_mnt_t *list) */
742 int
743 quota_mnt_type(const char *type)
744 {
745 if(strcmp(type, "ext3") == 0) return QMT_EXT3;
746 if(strcmp(type, "ext2") == 0) return QMT_EXT2;
747 if(strcmp(type, "ufs") == 0) return QMT_UFS;
748 if(strcmp(type, "vxfs") == 0) return QMT_VXFS;
749 if(strcmp(type, "zfs") == 0) return QMT_ZFS;
750 return QMT_UNKNOWN;
751 } /* int quota_mnt_type(const char *type) */