Module Name:    src
Committed By:   bouyer
Date:           Mon Jan 31 15:24:11 UTC 2011

Modified Files:
        src/sys/ufs/ufs [bouyer-quota2]: quota1_subr.c quota2_prop.c
            quota2_subr.c ufs_quota.c ufs_quota.h ufs_quota1.c ufs_quota2.c

Log Message:
Catch up with Q2V -> QL renaming
Enforce limits for quota2.
pass quota type (*QUOTA) and limit type (QL_*) to
KAUTH_REQ_SYSTEM_FS_QUOTA_NOLIMIT, to make it possible to skip
limit checks for some quota type only if a listener wants to.


To generate a diff of this commit:
cvs rdiff -u -r1.1.2.1 -r1.1.2.2 src/sys/ufs/ufs/quota1_subr.c \
    src/sys/ufs/ufs/ufs_quota1.c
cvs rdiff -u -r1.1.2.3 -r1.1.2.4 src/sys/ufs/ufs/quota2_prop.c \
    src/sys/ufs/ufs/quota2_subr.c src/sys/ufs/ufs/ufs_quota.h
cvs rdiff -u -r1.68.4.5 -r1.68.4.6 src/sys/ufs/ufs/ufs_quota.c
cvs rdiff -u -r1.1.2.5 -r1.1.2.6 src/sys/ufs/ufs/ufs_quota2.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/ufs/ufs/quota1_subr.c
diff -u src/sys/ufs/ufs/quota1_subr.c:1.1.2.1 src/sys/ufs/ufs/quota1_subr.c:1.1.2.2
--- src/sys/ufs/ufs/quota1_subr.c:1.1.2.1	Fri Jan 28 23:31:16 2011
+++ src/sys/ufs/ufs/quota1_subr.c	Mon Jan 31 15:24:10 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: quota1_subr.c,v 1.1.2.1 2011/01/28 23:31:16 bouyer Exp $ */
+/* $NetBSD: quota1_subr.c,v 1.1.2.2 2011/01/31 15:24:10 bouyer Exp $ */
 /*-
   * Copyright (c) 2010 Manuel Bouyer
   * All rights reserved.
@@ -28,7 +28,7 @@
   */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: quota1_subr.c,v 1.1.2.1 2011/01/28 23:31:16 bouyer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: quota1_subr.c,v 1.1.2.2 2011/01/31 15:24:10 bouyer Exp $");
 
 #include <sys/types.h>
 #include <machine/limits.h>
@@ -61,35 +61,35 @@
 void
 dqblk2q2e(const struct dqblk *dqblk, struct quota2_entry *q2e)
 {
-	q2e->q2e_val[Q2V_BLOCK].q2v_hardlimit =
+	q2e->q2e_val[QL_BLOCK].q2v_hardlimit =
 	    dqblk2q2e_limit(dqblk->dqb_bhardlimit);
-	q2e->q2e_val[Q2V_BLOCK].q2v_softlimit =
+	q2e->q2e_val[QL_BLOCK].q2v_softlimit =
 	    dqblk2q2e_limit(dqblk->dqb_bsoftlimit);
-	q2e->q2e_val[Q2V_BLOCK].q2v_cur       = dqblk->dqb_curblocks;
-	q2e->q2e_val[Q2V_BLOCK].q2v_time      = dqblk->dqb_btime;
+	q2e->q2e_val[QL_BLOCK].q2v_cur       = dqblk->dqb_curblocks;
+	q2e->q2e_val[QL_BLOCK].q2v_time      = dqblk->dqb_btime;
 
-	q2e->q2e_val[Q2V_FILE].q2v_hardlimit =
+	q2e->q2e_val[QL_FILE].q2v_hardlimit =
 	    dqblk2q2e_limit(dqblk->dqb_ihardlimit);
-	q2e->q2e_val[Q2V_FILE].q2v_softlimit =
+	q2e->q2e_val[QL_FILE].q2v_softlimit =
 	    dqblk2q2e_limit(dqblk->dqb_isoftlimit);
-	q2e->q2e_val[Q2V_FILE].q2v_cur       = dqblk->dqb_curinodes;
-	q2e->q2e_val[Q2V_FILE].q2v_time      = dqblk->dqb_itime;
+	q2e->q2e_val[QL_FILE].q2v_cur       = dqblk->dqb_curinodes;
+	q2e->q2e_val[QL_FILE].q2v_time      = dqblk->dqb_itime;
 }
 
 void
 q2e2dqblk(const struct quota2_entry *q2e, struct dqblk *dqblk)
 {
 	dqblk->dqb_bhardlimit =
-	    q2e2dqblk_limit(q2e->q2e_val[Q2V_BLOCK].q2v_hardlimit);
+	    q2e2dqblk_limit(q2e->q2e_val[QL_BLOCK].q2v_hardlimit);
 	dqblk->dqb_bsoftlimit =
-	    q2e2dqblk_limit(q2e->q2e_val[Q2V_BLOCK].q2v_softlimit);
-	dqblk->dqb_curblocks  = q2e->q2e_val[Q2V_BLOCK].q2v_cur;
-	dqblk->dqb_btime      = q2e->q2e_val[Q2V_BLOCK].q2v_time;
+	    q2e2dqblk_limit(q2e->q2e_val[QL_BLOCK].q2v_softlimit);
+	dqblk->dqb_curblocks  = q2e->q2e_val[QL_BLOCK].q2v_cur;
+	dqblk->dqb_btime      = q2e->q2e_val[QL_BLOCK].q2v_time;
 
 	dqblk->dqb_ihardlimit =
-	    q2e2dqblk_limit(q2e->q2e_val[Q2V_FILE].q2v_hardlimit);
+	    q2e2dqblk_limit(q2e->q2e_val[QL_FILE].q2v_hardlimit);
 	dqblk->dqb_isoftlimit =
-	    q2e2dqblk_limit(q2e->q2e_val[Q2V_FILE].q2v_softlimit);
-	dqblk->dqb_curinodes  = q2e->q2e_val[Q2V_FILE].q2v_cur;
-	dqblk->dqb_itime      = q2e->q2e_val[Q2V_FILE].q2v_time;
+	    q2e2dqblk_limit(q2e->q2e_val[QL_FILE].q2v_softlimit);
+	dqblk->dqb_curinodes  = q2e->q2e_val[QL_FILE].q2v_cur;
+	dqblk->dqb_itime      = q2e->q2e_val[QL_FILE].q2v_time;
 }
Index: src/sys/ufs/ufs/ufs_quota1.c
diff -u src/sys/ufs/ufs/ufs_quota1.c:1.1.2.1 src/sys/ufs/ufs/ufs_quota1.c:1.1.2.2
--- src/sys/ufs/ufs/ufs_quota1.c:1.1.2.1	Thu Jan 20 14:25:03 2011
+++ src/sys/ufs/ufs/ufs_quota1.c	Mon Jan 31 15:24:10 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: ufs_quota1.c,v 1.1.2.1 2011/01/20 14:25:03 bouyer Exp $	*/
+/*	$NetBSD: ufs_quota1.c,v 1.1.2.2 2011/01/31 15:24:10 bouyer Exp $	*/
 
 /*
  * Copyright (c) 1982, 1986, 1990, 1993, 1995
@@ -35,7 +35,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ufs_quota1.c,v 1.1.2.1 2011/01/20 14:25:03 bouyer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ufs_quota1.c,v 1.1.2.2 2011/01/31 15:24:10 bouyer Exp $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -88,12 +88,13 @@
 		}
 		return (0);
 	}
-	if ((flags & FORCE) == 0 &&
-	    kauth_authorize_system(cred, KAUTH_SYSTEM_FS_QUOTA,
-	    KAUTH_REQ_SYSTEM_FS_QUOTA_NOLIMIT, NULL, NULL, NULL) != 0) {
-		for (i = 0; i < MAXQUOTAS; i++) {
-			if ((dq = ip->i_dquot[i]) == NODQUOT)
-				continue;
+	for (i = 0; i < MAXQUOTAS; i++) {
+		if ((dq = ip->i_dquot[i]) == NODQUOT)
+			continue;
+		if ((flags & FORCE) == 0 &&
+		    kauth_authorize_system(cred, KAUTH_SYSTEM_FS_QUOTA,
+		    KAUTH_REQ_SYSTEM_FS_QUOTA_NOLIMIT, KAUTH_ARG(i),
+		    KAUTH_ARG(QL_BLOCK), NULL) != 0) {
 			mutex_enter(&dq->dq_interlock);
 			error = chkdqchg(ip, change, cred, i);
 			mutex_exit(&dq->dq_interlock);
@@ -195,12 +196,12 @@
 		}
 		return (0);
 	}
-	if ((flags & FORCE) == 0 && kauth_authorize_system(cred,
-	    KAUTH_SYSTEM_FS_QUOTA, KAUTH_REQ_SYSTEM_FS_QUOTA_NOLIMIT, NULL,
-	    NULL, NULL) != 0) {
-		for (i = 0; i < MAXQUOTAS; i++) {
-			if ((dq = ip->i_dquot[i]) == NODQUOT)
-				continue;
+	for (i = 0; i < MAXQUOTAS; i++) {
+		if ((dq = ip->i_dquot[i]) == NODQUOT)
+			continue;
+		if ((flags & FORCE) == 0 && kauth_authorize_system(cred,
+		    KAUTH_SYSTEM_FS_QUOTA, KAUTH_REQ_SYSTEM_FS_QUOTA_NOLIMIT,
+		    KAUTH_ARG(i), KAUTH_ARG(QL_FILE), NULL) != 0) {
 			mutex_enter(&dq->dq_interlock);
 			error = chkiqchg(ip, change, cred, i);
 			mutex_exit(&dq->dq_interlock);

Index: src/sys/ufs/ufs/quota2_prop.c
diff -u src/sys/ufs/ufs/quota2_prop.c:1.1.2.3 src/sys/ufs/ufs/quota2_prop.c:1.1.2.4
--- src/sys/ufs/ufs/quota2_prop.c:1.1.2.3	Sun Jan 30 00:25:19 2011
+++ src/sys/ufs/ufs/quota2_prop.c	Mon Jan 31 15:24:10 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: quota2_prop.c,v 1.1.2.3 2011/01/30 00:25:19 bouyer Exp $ */
+/* $NetBSD: quota2_prop.c,v 1.1.2.4 2011/01/31 15:24:10 bouyer Exp $ */
 /*-
   * Copyright (c) 2010 Manuel Bouyer
   * All rights reserved.
@@ -34,7 +34,7 @@
 
 #include <ufs/ufs/quota2_prop.h>
 
-const char *quota2_valnames[] = QUOTA2_VALNAMES_INIT;
+const char *quota2_valnames[] = INITQLNAMES;
 
 prop_dictionary_t
 prop_dictionary_get_dict(prop_dictionary_t dict, const char *key)
@@ -76,7 +76,7 @@
 {
 	int i, error;
 	prop_dictionary_t val;
-	for (i = 0; i < NQ2V; i++) {
+	for (i = 0; i < N_QL; i++) {
 		val = prop_dictionary_get_dict(data, quota2_valnames[i]);
 		if (val == NULL)
 			continue;
@@ -111,7 +111,7 @@
 {
 	int i, error;
 	prop_dictionary_t val;
-	for (i = 0; i < NQ2V; i++) {
+	for (i = 0; i < N_QL; i++) {
 		val = prop_dictionary_get_dict(data, quota2_valnames[i]);
 		if (val == NULL)
 			return EINVAL;
@@ -269,7 +269,7 @@
 			goto err;
 		}
 	}
-	for (i = 0; i < NQ2V; i++) {
+	for (i = 0; i < N_QL; i++) {
 		dict2 = q2vtoprop(&q2e->q2e_val[i]);
 		if (dict2 == NULL)
 			goto err;
Index: src/sys/ufs/ufs/quota2_subr.c
diff -u src/sys/ufs/ufs/quota2_subr.c:1.1.2.3 src/sys/ufs/ufs/quota2_subr.c:1.1.2.4
--- src/sys/ufs/ufs/quota2_subr.c:1.1.2.3	Fri Jan 28 23:30:34 2011
+++ src/sys/ufs/ufs/quota2_subr.c	Mon Jan 31 15:24:10 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: quota2_subr.c,v 1.1.2.3 2011/01/28 23:30:34 bouyer Exp $ */
+/* $NetBSD: quota2_subr.c,v 1.1.2.4 2011/01/31 15:24:10 bouyer Exp $ */
 /*-
   * Copyright (c) 2010 Manuel Bouyer
   * All rights reserved.
@@ -28,7 +28,7 @@
   */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: quota2_subr.c,v 1.1.2.3 2011/01/28 23:30:34 bouyer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: quota2_subr.c,v 1.1.2.4 2011/01/31 15:24:10 bouyer Exp $");
 
 #include <sys/param.h>
 #include <sys/time.h>
@@ -76,7 +76,7 @@
 	q2h->q2h_hash_shift = q2h_hash_shift;
 	q2h->q2h_hash_size = ufs_rw16(quota2_hash_size, ns);
 	/* setup defaut entry: unlimited, 7 days grace */
-	for (i = 0; i < NQ2V; i++) {
+	for (i = 0; i < N_QL; i++) {
 		q2h->q2h_defentry.q2e_val[i].q2v_hardlimit =
 		    q2h->q2h_defentry.q2e_val[i].q2v_softlimit =
 		    ufs_rw64(UQUAD_MAX, ns);
@@ -102,9 +102,9 @@
 quota2_ufs_rwq2e(const struct quota2_entry *s, struct quota2_entry *d,
 int needswap)
 {
-	quota2_ufs_rwq2v(&s->q2e_val[Q2V_BLOCK], &d->q2e_val[Q2V_BLOCK],
+	quota2_ufs_rwq2v(&s->q2e_val[QL_BLOCK], &d->q2e_val[QL_BLOCK],
 	    needswap);
-	quota2_ufs_rwq2v(&s->q2e_val[Q2V_FILE], &d->q2e_val[Q2V_FILE],
+	quota2_ufs_rwq2v(&s->q2e_val[QL_FILE], &d->q2e_val[QL_FILE],
 	    needswap);
 	d->q2e_uid = ufs_rw32(s->q2e_uid, needswap);
 }
Index: src/sys/ufs/ufs/ufs_quota.h
diff -u src/sys/ufs/ufs/ufs_quota.h:1.1.2.3 src/sys/ufs/ufs/ufs_quota.h:1.1.2.4
--- src/sys/ufs/ufs/ufs_quota.h:1.1.2.3	Sun Jan 30 00:25:20 2011
+++ src/sys/ufs/ufs/ufs_quota.h	Mon Jan 31 15:24:10 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: ufs_quota.h,v 1.1.2.3 2011/01/30 00:25:20 bouyer Exp $	*/
+/*	$NetBSD: ufs_quota.h,v 1.1.2.4 2011/01/31 15:24:10 bouyer Exp $	*/
 
 /*
  * Copyright (c) 1982, 1986, 1990, 1993, 1995
@@ -73,8 +73,7 @@
  */
 #define	DQ_MOD		0x04		/* this quota modified since read */
 #define	DQ_FAKE		0x08		/* no limits here, just usage */
-#define	DQ_BLKS		0x10		/* has been warned about blk limit */
-#define	DQ_INODS	0x20		/* has been warned about inode limit */
+#define	DQ_WARN(ltype)	(0x10 << ltype)	/* has been warned about "type" limit */
 /*
  * Shorthand notation.
  */

Index: src/sys/ufs/ufs/ufs_quota.c
diff -u src/sys/ufs/ufs/ufs_quota.c:1.68.4.5 src/sys/ufs/ufs/ufs_quota.c:1.68.4.6
--- src/sys/ufs/ufs/ufs_quota.c:1.68.4.5	Sun Jan 30 19:38:46 2011
+++ src/sys/ufs/ufs/ufs_quota.c	Mon Jan 31 15:24:10 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: ufs_quota.c,v 1.68.4.5 2011/01/30 19:38:46 bouyer Exp $	*/
+/*	$NetBSD: ufs_quota.c,v 1.68.4.6 2011/01/31 15:24:10 bouyer Exp $	*/
 
 /*
  * Copyright (c) 1982, 1986, 1990, 1993, 1995
@@ -35,7 +35,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ufs_quota.c,v 1.68.4.5 2011/01/30 19:38:46 bouyer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ufs_quota.c,v 1.68.4.6 2011/01/31 15:24:10 bouyer Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_quota.h"
@@ -682,7 +682,6 @@
 	return 0;
 }
 
-
 #ifdef DIAGNOSTIC
 /*
  * Check the hash chains for stray dquot's.

Index: src/sys/ufs/ufs/ufs_quota2.c
diff -u src/sys/ufs/ufs/ufs_quota2.c:1.1.2.5 src/sys/ufs/ufs/ufs_quota2.c:1.1.2.6
--- src/sys/ufs/ufs/ufs_quota2.c:1.1.2.5	Sun Jan 30 00:25:20 2011
+++ src/sys/ufs/ufs/ufs_quota2.c	Mon Jan 31 15:24:11 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: ufs_quota2.c,v 1.1.2.5 2011/01/30 00:25:20 bouyer Exp $ */
+/* $NetBSD: ufs_quota2.c,v 1.1.2.6 2011/01/31 15:24:11 bouyer Exp $ */
 /*-
   * Copyright (c) 2010 Manuel Bouyer
   * All rights reserved.
@@ -28,7 +28,7 @@
   */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ufs_quota2.c,v 1.1.2.5 2011/01/30 00:25:20 bouyer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ufs_quota2.c,v 1.1.2.6 2011/01/31 15:24:11 bouyer Exp $");
 
 #include <sys/buf.h>
 #include <sys/param.h>
@@ -71,7 +71,10 @@
     struct quota2_entry **, int);
 static int quota2_walk_list(struct ufsmount *, struct buf *, int,
     uint64_t *, int, void *,
-    int (*func)(struct ufsmount *, uint64_t *, struct quota2_entry *, uint64_t, void *));
+    int (*func)(struct ufsmount *, uint64_t *, struct quota2_entry *,
+      uint64_t, void *));
+
+static const char *valtypes[] = INITQLNAMES;
 
 static int
 getq2h(struct ufsmount *ump, int type,
@@ -354,8 +357,9 @@
 	int error;
 	struct buf *bp[MAXQUOTAS];
 	struct quota2_entry *q2e[MAXQUOTAS];
+	struct quota2_val *q2v;
 	struct dquot *dq;
-	int64_t ncurblks;
+	uint64_t ncurblks, soft, hard;
 	struct ufsmount *ump = ip->i_ump;
 	const int needswap = UFS_MPNEEDSWAP(ump);
 	int i;
@@ -373,40 +377,103 @@
 		}
 		return 0;
 	}
-	if (change < 0 || change > 0) {
+	if (change < 0) {
 		for (i = 0; i < MAXQUOTAS; i++) {
 			dq = ip->i_dquot[i];
 			if (dq == NODQUOT)
 				continue;
-			if (q2e[i] == NULL)
+			if (q2e[i] == NULL) {
+				mutex_exit(&dq->dq_interlock);
 				continue;
-			ncurblks =
-			    ufs_rw64(q2e[i]->q2e_val[vtype].q2v_cur, needswap);
-			if (change < 0 && ncurblks < -change)
+			}
+			q2v = &q2e[i]->q2e_val[vtype];
+			ncurblks = ufs_rw64(q2v->q2v_cur, needswap);
+			if (ncurblks < -change)
 				ncurblks = 0;
 			else
 				ncurblks += change;
-			q2e[i]->q2e_val[vtype].q2v_cur =
-			    ufs_rw64(ncurblks, needswap);
+			q2v->q2v_cur = ufs_rw64(ncurblks, needswap);
 			VOP_BWRITE(bp[i]);
 			mutex_exit(&dq->dq_interlock);
 		}
 		return 0;
 	}
+	/* see if the allocation is allowed */
+	for (i = 0; i < MAXQUOTAS; i++) {
+		if ((flags & FORCE) != 0 ||
+		    kauth_authorize_system(cred, KAUTH_SYSTEM_FS_QUOTA,
+		    KAUTH_REQ_SYSTEM_FS_QUOTA_NOLIMIT,
+		    KAUTH_ARG(i), KAUTH_ARG(vtype), NULL) == 0) {
+			/* don't check this limit */
+			continue;
+		}
+		dq = ip->i_dquot[i];
+		if (dq == NODQUOT)
+			continue;
+		KASSERT(q2e[i] != NULL);
+		q2v = &q2e[i]->q2e_val[vtype];
+		ncurblks = ufs_rw64(q2v->q2v_cur, needswap);
+		soft = ufs_rw64(q2v->q2v_softlimit, needswap);
+		hard = ufs_rw64(q2v->q2v_hardlimit, needswap);
+		if (ncurblks + change >= hard) {
+			if ((dq->dq_flags & DQ_WARN(vtype)) == 0) {
+				uprintf("\n%s: write failed, %s %s limit "
+				    "reached\n",
+				    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
+				    quotatypes[i], valtypes[vtype]);
+				dq->dq_flags |= DQ_WARN(vtype);
+			}
+			error = EDQUOT;
+		} else if (ncurblks >= soft &&
+		    time_second > ufs_rw64(q2v->q2v_time, needswap)) {
+			if ((dq->dq_flags & DQ_WARN(vtype)) == 0) {
+				uprintf("\n%s: write failed, %s %s "
+				    "limit reached\n",
+				    ump->um_mountp->mnt_stat.f_mntonname,
+				    quotatypes[i], valtypes[vtype]);
+				dq->dq_flags |= DQ_WARN(vtype);
+			}
+			error = EDQUOT;
+		}
+	}
 
-	return 0;
+	/* now do the allocation if allowed */
+	for (i = 0; i < MAXQUOTAS; i++) {
+		dq = ip->i_dquot[i];
+		if (dq == NODQUOT)
+			continue;
+		KASSERT(q2e[i] != NULL);
+		if (error == 0) {
+			q2v = &q2e[i]->q2e_val[vtype];
+			ncurblks = ufs_rw64(q2v->q2v_cur, needswap);
+			soft = ufs_rw64(q2v->q2v_softlimit, needswap);
+			if (ncurblks < soft && (ncurblks + change) >= soft) {
+				q2v->q2v_time = ufs_rw64(time_second +
+				    ufs_rw64(q2v->q2v_grace, needswap),
+				    needswap);
+				uprintf("\n%s: warning, %s %s quota exceeded\n",
+				    ump->um_mountp->mnt_stat.f_mntonname,
+				    quotatypes[i], valtypes[vtype]);
+			}
+			q2v->q2v_cur = ufs_rw64(ncurblks + change, needswap);
+			VOP_BWRITE(bp[i]);
+		} else
+			brelse(bp[i], 0);
+		mutex_exit(&dq->dq_interlock);
+	}
+	return error;
 }
 
 int
 chkdq2(struct inode *ip, int64_t change, kauth_cred_t cred, int flags)
 {
-	return quota2_check(ip, Q2V_BLOCK, change, cred, flags);
+	return quota2_check(ip, QL_BLOCK, change, cred, flags);
 }
 
 int
 chkiq2(struct inode *ip, int32_t change, kauth_cred_t cred, int flags)
 {
-	return quota2_check(ip, Q2V_FILE, change, cred, flags);
+	return quota2_check(ip, QL_FILE, change, cred, flags);
 }
 
 int

Reply via email to