When the quotacheck has not been run the quota code may have to
authorMatthew Dillon <dillon@dragonflybsd.org>
Tue, 6 Nov 2007 17:11:38 +0000 (17:11 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Tue, 6 Nov 2007 17:11:38 +0000 (17:11 +0000)
allocate blocks in the userquota file itself.  This will deadlock the
quota system.

Disallow adjustments of quotas related to operations on the userquota file
itself, and generate a warning to the console.

Reported-by: David W <dpwalters@carolina.rr.com>
sys/vfs/ufs/quota.h
sys/vfs/ufs/ufs_quota.c

index 4ffcfbf..7ede91e 100644 (file)
@@ -35,7 +35,7 @@
  *
  *     @(#)quota.h     8.3 (Berkeley) 8/19/94
  * $FreeBSD: src/sys/ufs/ufs/quota.h,v 1.15.2.1 2003/02/27 12:04:13 das Exp $
- * $DragonFly: src/sys/vfs/ufs/quota.h,v 1.8 2006/05/06 18:48:53 dillon Exp $
+ * $DragonFly: src/sys/vfs/ufs/quota.h,v 1.9 2007/11/06 17:11:38 dillon Exp $
  */
 
 #ifndef _VFS_UFS_QUOTA_H_
@@ -125,7 +125,7 @@ struct ufs_dquot {
        uint16_t dq_flags;              /* flags, see below */
        uint16_t dq_type;               /* quota type of this dquot */
        uint32_t dq_cnt;                /* count of active references */
-       uint32_t dq_id;         /* identifier this applies to */
+       uint32_t dq_id;                 /* identifier this applies to */
        struct  ufsmount *dq_ump;       /* filesystem that this is taken from */
        struct  ufs_dqblk dq_dqb;       /* actual usage & quotas */
 };
index daf8852..58c8c31 100644 (file)
@@ -35,7 +35,7 @@
  *
  *     @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95
  * $FreeBSD: src/sys/ufs/ufs/ufs_quota.c,v 1.27.2.3 2002/01/15 10:33:32 phk Exp $
- * $DragonFly: src/sys/vfs/ufs/ufs_quota.c,v 1.24 2006/09/05 00:55:51 dillon Exp $
+ * $DragonFly: src/sys/vfs/ufs/ufs_quota.c,v 1.25 2007/11/06 17:11:38 dillon Exp $
  */
 
 #include <sys/param.h>
@@ -66,6 +66,7 @@ static int ufs_dqget (struct vnode *,
                u_long, struct ufsmount *, int, struct ufs_dquot **);
 static int ufs_dqsync (struct vnode *, struct ufs_dquot *);
 static void ufs_dqflush (struct vnode *);
+static void ufs_quotawarn(struct ufs_dquot *dq);
 
 #ifdef DIAGNOSTIC
 static void ufs_dqref (struct ufs_dquot *);
@@ -127,6 +128,10 @@ ufs_chkdq(struct inode *ip, long change, struct ucred *cred, int flags)
                for (i = 0; i < MAXQUOTAS; i++) {
                        if ((dq = ip->i_dquot[i]) == NODQUOT)
                                continue;
+                       if (dq->dq_ump->um_quotas[dq->dq_type] == ip->i_vnode) {
+                               ufs_quotawarn(dq);
+                               continue;
+                       }
                        while (dq->dq_flags & DQ_LOCK) {
                                dq->dq_flags |= DQ_WANT;
                                (void) tsleep((caddr_t)dq, 0, "chkdq1", 0);
@@ -145,6 +150,10 @@ ufs_chkdq(struct inode *ip, long change, struct ucred *cred, int flags)
                for (i = 0; i < MAXQUOTAS; i++) {
                        if ((dq = ip->i_dquot[i]) == NODQUOT)
                                continue;
+                       if (dq->dq_ump->um_quotas[dq->dq_type] == ip->i_vnode) {
+                               ufs_quotawarn(dq);
+                               continue;
+                       }
                        error = ufs_chkdqchg(ip, change, cred, i);
                        if (error)
                                return (error);
@@ -153,6 +162,10 @@ ufs_chkdq(struct inode *ip, long change, struct ucred *cred, int flags)
        for (i = 0; i < MAXQUOTAS; i++) {
                if ((dq = ip->i_dquot[i]) == NODQUOT)
                        continue;
+               if (dq->dq_ump->um_quotas[dq->dq_type] == ip->i_vnode) {
+                       ufs_quotawarn(dq);
+                       continue;
+               }
                while (dq->dq_flags & DQ_LOCK) {
                        dq->dq_flags |= DQ_WANT;
                        (void) tsleep((caddr_t)dq, 0, "chkdq2", 0);
@@ -240,6 +253,10 @@ ufs_chkiq(struct inode *ip, long change, struct ucred *cred, int flags)
                for (i = 0; i < MAXQUOTAS; i++) {
                        if ((dq = ip->i_dquot[i]) == NODQUOT)
                                continue;
+                       if (dq->dq_ump->um_quotas[dq->dq_type] == ip->i_vnode) {
+                               ufs_quotawarn(dq);
+                               continue;
+                       }
                        while (dq->dq_flags & DQ_LOCK) {
                                dq->dq_flags |= DQ_WANT;
                                (void) tsleep((caddr_t)dq, 0, "chkiq1", 0);
@@ -258,6 +275,10 @@ ufs_chkiq(struct inode *ip, long change, struct ucred *cred, int flags)
                for (i = 0; i < MAXQUOTAS; i++) {
                        if ((dq = ip->i_dquot[i]) == NODQUOT)
                                continue;
+                       if (dq->dq_ump->um_quotas[dq->dq_type] == ip->i_vnode) {
+                               ufs_quotawarn(dq);
+                               continue;
+                       }
                        error = ufs_chkiqchg(ip, change, cred, i);
                        if (error)
                                return (error);
@@ -266,6 +287,10 @@ ufs_chkiq(struct inode *ip, long change, struct ucred *cred, int flags)
        for (i = 0; i < MAXQUOTAS; i++) {
                if ((dq = ip->i_dquot[i]) == NODQUOT)
                        continue;
+               if (dq->dq_ump->um_quotas[dq->dq_type] == ip->i_vnode) {
+                       ufs_quotawarn(dq);
+                       continue;
+               }
                while (dq->dq_flags & DQ_LOCK) {
                        dq->dq_flags |= DQ_WANT;
                        (void) tsleep((caddr_t)dq, 0, "chkiq2", 0);
@@ -333,6 +358,23 @@ ufs_chkiqchg(struct inode *ip, long change, struct ucred *cred, int type)
        return (0);
 }
 
+/*
+ * To avoid a deadlock we disallow quota operations on the quota file itself.
+ * This generally means that quotacheck was not run on the filesystem.
+ */
+static
+void
+ufs_quotawarn(struct ufs_dquot *dq)
+{
+       static int dqticks;
+       if (dqticks / hz != ticks / hz) {
+               dqticks = ticks / hz;
+               uprintf("%s: warning, quota file expanded, quotacheck "
+                       "was not run!\n",
+                       dq->dq_ump->um_mountp->mnt_stat.f_mntfromname); 
+       }
+}
+
 #ifdef DIAGNOSTIC
 /*
  * On filesystems with quotas enabled, it is an error for a file to change