]> arthur.barton.de Git - netatalk.git/blobdiff - etc/afpd/quota.c
quotas: improve Linux quota behaviour
[netatalk.git] / etc / afpd / quota.c
index 66c4948999ce84e00c9a16726afdc5989b12f51f..bab14e1345baf9a9f8b9447518ab1233b7f7650b 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: quota.c,v 1.35 2010-04-03 07:11:35 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
 #include <atalk/logger.h>
 #include <atalk/afp.h>
 #include <atalk/compat.h>
+#include <atalk/unix.h>
+#include <atalk/util.h>
 
 #include "auth.h"
 #include "volume.h"
 #include "unix.h"
 
+#ifdef HAVE_LIBQUOTA
+#include <quota/quota.h>
+
+static int
+getfreespace(const AFPObj *obj, struct vol *vol, VolSpace *bfree, VolSpace *btotal,
+            uid_t uid, const char *classq)
+{
+       int retq;
+       struct ufs_quota_entry ufsq[QUOTA_NLIMITS];
+       time_t now;
+
+       if (time(&now) == -1) {
+               LOG(log_info, logtype_afpd, "time(): %s",
+                   strerror(errno));
+               return -1;
+       }
+
+    become_root();
+
+       if ((retq = getfsquota(obj, vol, ufsq, uid, classq)) < 0) {
+               LOG(log_info, logtype_afpd, "getfsquota(%s, %s): %s",
+                   vol->v_path, classq, strerror(errno));
+       }
+
+    unbecome_root();
+
+       if (retq < 1)
+               return retq;
+
+       switch(QL_STATUS(quota_check_limit(ufsq[QUOTA_LIMIT_BLOCK].ufsqe_cur, 1,
+           ufsq[QUOTA_LIMIT_BLOCK].ufsqe_softlimit,
+           ufsq[QUOTA_LIMIT_BLOCK].ufsqe_hardlimit,
+           ufsq[QUOTA_LIMIT_BLOCK].ufsqe_time, now))) {
+       case QL_S_DENY_HARD:
+       case QL_S_DENY_GRACE:
+               *bfree = 0;
+               *btotal = dbtob(ufsq[QUOTA_LIMIT_BLOCK].ufsqe_cur);
+               break;
+       default:
+               *bfree = dbtob(ufsq[QUOTA_LIMIT_BLOCK].ufsqe_hardlimit -
+                   ufsq[QUOTA_LIMIT_BLOCK].ufsqe_cur);
+               *btotal = dbtob(ufsq[QUOTA_LIMIT_BLOCK].ufsqe_hardlimit);
+               break;
+       }
+       return 1;
+}
+
+int uquota_getvolspace(const AFPObj *obj, struct vol *vol, VolSpace *bfree, VolSpace *btotal, const u_int32_t bsize)
+{
+       int uretq, gretq;
+       VolSpace ubfree, ubtotal;
+       VolSpace gbfree, gbtotal;
+
+       uretq = getfreespace(obj, vol, &ubfree, &ubtotal,
+                            uuid, QUOTADICT_CLASS_USER);
+       LOG(log_info, logtype_afpd, "getfsquota(%s): %d %d",
+           vol->v_path, (int)ubfree, (int)ubtotal);
+       if (obj->ngroups >= 1) {
+               gretq = getfreespace(vol, &ubfree, &ubtotal,
+                   obj->groups[0], QUOTADICT_CLASS_GROUP);
+       } else
+               gretq = -1;
+       if (uretq < 1 && gretq < 1) { /* no quota for this fs */
+               return AFPERR_PARAM;
+       }
+       if (uretq < 1) {
+               /* use group quotas */
+               *bfree = gbfree;
+               *btotal = gbtotal;
+       } else if (gretq < 1) {
+               /* use user quotas */
+               *bfree = ubfree;
+               *btotal = ubtotal;
+       } else {
+               /* return smallest remaining space of user and group */
+               if (ubfree < gbfree) {
+                       *bfree = ubfree;
+                       *btotal = ubtotal;
+               } else {
+                       *bfree = gbfree;
+                       *btotal = gbtotal;
+               }
+       }
+       return AFP_OK;
+
+}
+
+#else /* HAVE_LIBQUOTA */
+
 /*
 #define DEBUG_QUOTA 0
 */
@@ -280,7 +370,7 @@ mountp( char *file, int *nfs)
     dev_t                      devno;
     static struct mnttab       mnt;
 
-    if ( lstat( file, &sb ) < 0 ) {
+    if (stat(file, &sb) < 0) {
         return( NULL );
     }
     devno = sb.st_dev;
@@ -291,14 +381,14 @@ mountp( char *file, int *nfs)
 
     while ( getmntent( mtab, &mnt ) == 0 ) {
         /* local fs */
-        if ( (lstat( mnt.mnt_special, &sb ) == 0) && (devno == sb.st_rdev)) {
+        if ( (stat( mnt.mnt_special, &sb ) == 0) && (devno == sb.st_rdev)) {
             fclose( mtab );
             return mnt.mnt_mountp;
         }
 
         /* check for nfs. we probably should use
          * strcmp(mnt.mnt_fstype, MNTTYPE_NFS), but that's not as fast. */
-        if ((lstat(mnt.mnt_mountp, &sb) == 0) && (devno == sb.st_dev) &&
+        if ((stat(mnt.mnt_mountp, &sb) == 0) && (devno == sb.st_dev) &&
                 strchr(mnt.mnt_special, ':')) {
             *nfs = 1;
             fclose( mtab );
@@ -368,7 +458,7 @@ special(char *file, int *nfs)
     struct mntent      *mnt;
     int                found=0;
 
-    if ( lstat( file, &sb ) < 0 ) {
+    if (stat(file, &sb) < 0 ) {
         return( NULL );
     }
     devno = sb.st_dev;
@@ -379,14 +469,14 @@ special(char *file, int *nfs)
 
     while (( mnt = getmntent( mtab )) != NULL ) {
         /* check for local fs */
-        if ( (lstat( mnt->mnt_fsname, &sb ) == 0) && devno == sb.st_rdev) {
+        if ( (stat( mnt->mnt_fsname, &sb ) == 0) && devno == sb.st_rdev) {
            found = 1;
            break;
         }
 
         /* check for an nfs mount entry. the alternative is to use
         * strcmp(mnt->mnt_type, MNTTYPE_NFS) instead of the strchr. */
-        if ((lstat(mnt->mnt_dir, &sb) == 0) && (devno == sb.st_dev) &&
+        if ((stat(mnt->mnt_dir, &sb) == 0) && (devno == sb.st_dev) &&
                 strchr(mnt->mnt_fsname, ':')) {
             *nfs = 1;
            found = 1;
@@ -411,7 +501,7 @@ special(char *file, int *nfs)
 #endif /* __svr4__ */
 
 
-static int getfsquota(struct vol *vol, const int uid, struct dqblk *dq)
+static int getfsquota(const AFPObj *obj, struct vol *vol, const int uid, struct dqblk *dq)
 
 {
        struct dqblk dqg;
@@ -420,6 +510,7 @@ static int getfsquota(struct vol *vol, const int uid, struct dqblk *dq)
     struct quotctl      qc;
 #endif
 
+    memset(dq, 0, sizeof(struct dqblk));
     memset(&dqg, 0, sizeof(dqg));
        
 #ifdef __svr4__
@@ -451,34 +542,26 @@ static int getfsquota(struct vol *vol, const int uid, struct dqblk *dq)
 #endif /* TRU64 */
 
 #ifdef BSD4_4
-    if ( seteuid( getuid() ) == 0 ) {
+    become_root();
         if ( quotactl( vol->v_path, QCMD(Q_GETQUOTA,USRQUOTA),
                        uid, (char *)dq ) != 0 ) {
             /* try group quotas */
-            if (ngroups >= 1) {
+            if (obj->ngroups >= 1) {
                 if ( quotactl(vol->v_path, QCMD(Q_GETQUOTA, GRPQUOTA),
-                              groups[0], (char *) &dqg) != 0 ) {
-                    seteuid( uid );
+                              obj->groups[0], (char *) &dqg) != 0 ) {
+                    unbecome_root();
                     return( AFPERR_PARAM );
                 }
             }
         }
-        seteuid( uid );
-    }
-
-#elif defined(TRU64)
-    if ( seteuid( getuid() ) == 0 ) {
-        if ( quotactl( vol->v_path, QCMD(Q_GETQUOTA, USRQUOTA),
-                       uid, (char *)dq ) != 0 ) {
-            seteuid( uid );
-            return ( AFPERR_PARAM );
-        }
-        seteuid( uid );
+        unbecome_root();
     }
 
 #else /* BSD4_4 */
     if (get_linux_quota (WANT_USER_QUOTA, vol->v_gvs, uid, dq) !=0) {
-        return( AFPERR_PARAM );
+#ifdef DEBUG_QUOTA
+        LOG(log_debug, logtype_afpd, "user quota did not work!" );
+#endif /* DEBUG_QUOTA */
     }
 
     if (get_linux_quota(WANT_GROUP_QUOTA, vol->v_gvs, getegid(),  &dqg) != 0) {
@@ -510,10 +593,15 @@ static int getfsquota(struct vol *vol, const int uid, struct dqblk *dq)
       ) /* if */
     {
         /* use group quota limits rather than user limits */
-        dq->dqb_curblocks = dqg.dqb_curblocks;
         dq->dqb_bhardlimit = dqg.dqb_bhardlimit;
         dq->dqb_bsoftlimit = dqg.dqb_bsoftlimit;
-        dq->dqb_btimelimit = dqg.dqb_btimelimit;
+        dq->dqb_curblocks = dqg.dqb_curblocks;
+        dq->dqb_ihardlimit = dqg.dqb_ihardlimit;
+        dq->dqb_isoftlimit = dqg.dqb_isoftlimit;
+        dq->dqb_curinodes = dqg.dqb_curinodes;
+        dq->dqb_btime = dqg.dqb_btime;
+        dq->dqb_itime = dqg.dqb_itime;
+        dq->bsize = dqg.bsize;
     } /* if */
 
 #endif /* TRU64 */
@@ -525,7 +613,7 @@ static int getfsquota(struct vol *vol, const int uid, struct dqblk *dq)
 }
 
 
-static int getquota( struct vol *vol, struct dqblk *dq, const uint32_t bsize)
+static int getquota(const AFPObj *obj, struct vol *vol, struct dqblk *dq, const uint32_t bsize)
 {
     char *p;
 
@@ -600,11 +688,11 @@ static int getquota( struct vol *vol, struct dqblk *dq, const uint32_t bsize)
            return getnfsquota(vol, uuid, bsize, dq);
     } else
        /* local filesystem */
-       return getfsquota(vol, uuid, dq);
+      return getfsquota(obj, vol, obj->uid, dq);
           
 #else /* TRU64 */
-    return vol->v_nfs ? getnfsquota(vol, uuid, bsize, dq) :
-           getfsquota(vol, uuid, dq);
+    return vol->v_nfs ? getnfsquota(vol, obj->uid, bsize, dq) :
+      getfsquota(obj, vol, obj->uid, dq);
 #endif /* TRU64 */
 }
 
@@ -663,14 +751,14 @@ static int overquota( struct dqblk *dqblk)
 #define tobytes(a, b)  dbtob((VolSpace) (a))
 #endif
 
-int uquota_getvolspace( struct vol *vol, VolSpace *bfree, VolSpace *btotal, const uint32_t bsize)
+int uquota_getvolspace(const AFPObj *obj, struct vol *vol, VolSpace *bfree, VolSpace *btotal, const uint32_t bsize)
 {
        uint64_t this_bsize;
        struct dqblk dqblk;
 
        this_bsize = bsize;
                        
-       if (getquota( vol, &dqblk, bsize) != 0 ) {
+       if (getquota(obj, vol, &dqblk, bsize) != 0 ) {
                return( AFPERR_PARAM );
        }
 
@@ -722,4 +810,5 @@ int uquota_getvolspace( struct vol *vol, VolSpace *bfree, VolSpace *btotal, cons
 
        return( AFP_OK );
 }
+#endif /* HAVE_LIBQUOTA */
 #endif