$NetBSD: patch-ab,v 1.10 2012/05/12 23:10:30 dholland Exp $

- fix package's version number
- remove a debug printout
- need unistd.h for some things
- support dragonfly
- support linux 2.6+ with slightly different API
- support netbsd-3+ with statvfs
- support netbsd-6+ with libquota
- fix solaris code
- ruby API fixes
- ...?

--- quota.c.orig	2002-03-30 14:59:12.000000000 +0000
+++ quota.c
@@ -5,17 +5,25 @@
 
 #include "ruby.h"
 
-#define RUBY_QUOTA_VERSION "0.4.1"
+#define RUBY_QUOTA_VERSION "0.5.1"
 
-#ifdef HAVE_LINUX_QUOTA_H       /* for linux-2.4.x */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_LINUX_QUOTA_H       /* for linux */
 # define USE_LINUX_QUOTA
 #endif
 #ifdef HAVE_SYS_FS_UFS_QUOTA_H  /* for Solaris-2.6,7,8 */
 # define USE_SOLARIS_QUOTA
 #endif
-#ifdef HAVE_UFS_UFS_QUOTA_H     /* for *BSD */
+#ifdef HAVE_QUOTA_H		/* for NetBSD-6 and up */
+# define USE_NETBSD_QUOTA
+#else
+#ifdef HAVE_UFS_UFS_QUOTA_H     /* for traditional *BSD */
 # define USE_BSD_QUOTA
 #endif
+#endif
 
 #ifdef USE_LINUX_QUOTA
 #ifdef HAVE_LINUX_TYPES_H
@@ -29,10 +37,16 @@
 #  include <sys/quota.h>
 #endif
 #include <linux/version.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+#  define USE_LINUX_QUOTA_26
+#  define qid_t uid_t
+#  define dqblk if_dqblk
+#else
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
 #  define USE_LINUX_QUOTA_24
 #  define uid_t qid_t
 #  define dqblk disk_dqblk
+# endif
 #endif
 #endif
 
@@ -42,6 +56,12 @@
 #include <sys/fs/ufs_quota.h>
 #endif
 
+#ifdef USE_NETBSD_QUOTA
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <quota.h>
+#endif
+
 #ifdef USE_BSD_QUOTA
 #include <sys/types.h>
 #include <sys/fcntl.h>
@@ -51,6 +71,12 @@
 #if defined(SYS_UCRED_H)
 # include <sys/ucred.h>  /* required by NetBSD,FreeBSD */
 #endif
+#if defined(__DragonFly__)
+#  include <sys/param.h>
+#  if __DragonFly_version >= 160000
+#    define dqblk ufs_dqblk
+#  endif
+#endif
 #endif
 
 static VALUE rb_mQuota;
@@ -144,7 +170,6 @@ rb_quotactl(int cmd, char *dev, VALUE vu
   uid_t uid;
 
   get_uid(vuid, &uid, &is_gid);
-  printf("cmd = %d, dev = %s, uid = %d, gid? = %d\n", cmd, dev, uid, is_gid);
   if( is_gid ){
     return quotactl(QCMD(cmd,GRPQUOTA),dev,(qid_t)uid,addr);
   }
@@ -152,14 +177,18 @@ rb_quotactl(int cmd, char *dev, VALUE vu
     return quotactl(QCMD(cmd,USRQUOTA),dev,(qid_t)uid,addr);
   };
 };
-#elif defined(USE_BSD_QUOTA) /* for *BSD */
+#elif defined(USE_BSD_QUOTA) /* for traditional *BSD */
 static int
 rb_quotactl(int cmd, char *dev, VALUE vuid, caddr_t addr)
 {
   char *path;
   int is_gid;
   uid_t uid;
+#if defined(HAVE_SYS_STATVFS_H) && !defined(__DragonFly__)
+  struct statvfs *buff;
+#else
   struct statfs *buff;
+#endif
   int i, count, ret;
   
   buff = 0;
@@ -187,12 +216,16 @@ rb_quotactl(int cmd, char *dev, VALUE vu
 static int
 rb_quotactl(int cmd, char *dev, VALUE vuid, caddr_t addr)
 {
-  struct quotctl qctl = {cmd, uid, addr};
+  struct quotctl qctl;
   int fd;
   uid_t uid;
 
   get_uid(vuid, &uid, 0);
 
+  qctl.op = cmd;
+  qctl.uid = uid;
+  qctl.addr = addr;
+
   switch( cmd ){
   case Q_QUOTAON:
   case Q_QUOTAOFF:
@@ -225,17 +258,217 @@ rb_quotactl(int cmd, char *dev, VALUE vu
 };
 #endif
 
+#ifdef USE_NETBSD_QUOTA
+
+static struct quotahandle *
+rb_quotaopen(char *dev)
+{
+  char *path;
+#if defined(HAVE_SYS_STATVFS_H) && !defined(__DragonFly__)
+  struct statvfs *buff;
+#else
+  struct statfs *buff;
+#endif
+  int i, count;
+
+  buff = 0;
+  path = dev;
+  count = getmntinfo(&buff, MNT_WAIT);
+  for( i=0; i<count; i++ ){
+    if( strcmp(buff[i].f_mntfromname, dev) == 0 ){
+      path = buff[i].f_mntonname;
+      break;
+    };
+  };
+
+  return quota_open(path);
+}
+
+static int
+rb_quotaget(char *dev, VALUE vuid, struct quotaval *blocks, struct quotaval *files)
+{
+  struct quotahandle *qh;
+
+  int is_gid;
+  uid_t uid;
+  int ret;
+  struct quotakey qk;
+
+  get_uid(vuid, &uid, &is_gid);
+  qk.qk_idtype = is_gid ? QUOTA_IDTYPE_GROUP : QUOTA_IDTYPE_USER;
+  qk.qk_id = uid;
+  qk.qk_objtype = QUOTA_OBJTYPE_BLOCKS;
+
+  qh = rb_quotaopen(dev);  
+  if (qh == NULL) {
+    return -1;
+  }
+
+  ret = quota_get(qh, &qk, blocks);
+  if (ret) {
+    quota_close(qh);
+    return -1;
+  }
+
+  qk.qk_objtype = QUOTA_OBJTYPE_FILES;
+  ret = quota_get(qh, &qk, files);
+  if (ret) {
+    quota_close(qh);
+    return -1;
+  }
+
+  quota_close(qh);
+  return 0;
+};
+
+static int
+rb_quotaoff(char *dev)
+{
+  struct quotahandle *qh;
+  int ret1, ret2;
+
+  qh = rb_quotaopen(dev);  
+  if (qh == NULL) {
+    return -1;
+  }
+  ret1 = quota_quotaoff(qh, QUOTA_IDTYPE_USER);
+  ret2 = quota_quotaoff(qh, QUOTA_IDTYPE_GROUP);
+  quota_close(qh);
+  if (ret1 < 0 || ret2 < 0) {
+    return -1;
+  }
+  return 0;
+}
+
+static int
+rb_quotaon(char *dev)
+{
+  struct quotahandle *qh;
+  int ret1, ret2;
+
+  qh = rb_quotaopen(dev);  
+  if (qh == NULL) {
+    return -1;
+  }
+  ret1 = quota_quotaon(qh, QUOTA_IDTYPE_USER);
+  ret2 = quota_quotaon(qh, QUOTA_IDTYPE_GROUP);
+  quota_close(qh);
+  /* fail only if *both* idtypes failed */
+  if (ret1 < 0 && ret2 < 0) {
+    return -1;
+  }
+  return 0;
+}
+
+static int
+rb_quotaput(char *dev, VALUE vuid, const struct quotaval *blocks, const struct quotaval *files)
+{
+  struct quotahandle *qh;
+
+  int is_gid;
+  uid_t uid;
+  int ret;
+  struct quotakey qk;
+
+  get_uid(vuid, &uid, &is_gid);
+  qk.qk_idtype = is_gid ? QUOTA_IDTYPE_GROUP : QUOTA_IDTYPE_USER;
+  qk.qk_id = uid;
+  qk.qk_objtype = QUOTA_OBJTYPE_BLOCKS;
+
+  qh = rb_quotaopen(dev);  
+  if (qh == NULL) {
+    return -1;
+  }
+
+  ret = quota_put(qh, &qk, blocks);
+  if (ret) {
+    quota_close(qh);
+    return -1;
+  }
+
+  qk.qk_objtype = QUOTA_OBJTYPE_FILES;
+  ret = quota_put(qh, &qk, files);
+  if (ret) {
+    quota_close(qh);
+    return -1;
+  }
+
+  quota_close(qh);
+  return 0;
+};
+
+#endif /* USE_NETBSD_QUOTA */
+
+#ifdef USE_NETBSD_QUOTA
+
+void
+rb_diskquota_get(VALUE dqb, struct quotaval *c_blocks, struct quotaval *c_files)
+{
+  VALUE v;
+
+#define GetMemberU(mem) \
+        ((v = rb_struct_getmember(dqb,rb_intern(mem))) == Qnil) ? 0 : (NUM2ULL(v))
+#define GetMemberS(mem) \
+        ((v = rb_struct_getmember(dqb,rb_intern(mem))) == Qnil) ? 0 : (NUM2LL(v))
+
+  c_blocks->qv_hardlimit  = GetMemberU("bhardlimit");
+  c_blocks->qv_softlimit  = GetMemberU("bsoftlimit");
+  c_blocks->qv_usage      = GetMemberU("curblocks");
+  c_blocks->qv_expiretime = GetMemberS("btimelimit");
+  c_blocks->qv_grace      = GetMemberS("bgrace");
+  c_files->qv_hardlimit   = GetMemberU("ihardlimit");
+  c_files->qv_softlimit   = GetMemberU("isoftlimit");
+  c_files->qv_usage       = GetMemberU("curinodes");
+  c_files->qv_expiretime  = GetMemberS("itimelimit");
+  c_files->qv_grace       = GetMemberS("igrace");
+
+#undef GetMemberU
+#undef GetMemberS
+}
+
+VALUE
+rb_diskquota_new(struct quotaval *c_blocks, struct quotaval *c_files)
+{
+  VALUE dqb;
+
+  dqb = rb_struct_new(rb_sDiskQuota,
+		      ULL2NUM(c_blocks->qv_hardlimit),
+		      ULL2NUM(c_blocks->qv_softlimit),
+		      ULL2NUM(c_blocks->qv_usage),
+		      ULL2NUM(c_files->qv_hardlimit),
+		      ULL2NUM(c_files->qv_softlimit),
+		      ULL2NUM(c_files->qv_usage),
+		      LL2NUM(c_blocks->qv_expiretime),
+		      LL2NUM(c_files->qv_expiretime),
+#if 0 /* not yet */
+		      LL2NUM(c_blocks->qv_grace),
+		      LL2NUM(c_files->qv_grace),
+#endif
+		      0);
+  return dqb;
+}
+
+#else /* not USE_NETBSD_QUOTA */
+
 void
 rb_diskquota_get(VALUE dqb, struct dqblk * c_dqb)
 {
   VALUE v;
 
+#if defined(USE_LINUX_QUOTA) && \
+	(defined(USE_LINUX_QUOTA_24) || defined(USE_LINUX_QUOTA_26))
+#define GetMember(mem) \
+        ((v = rb_struct_getmember(dqb,rb_intern(mem))) == Qnil) ? 0 : (NUM2ULL(v))
+#else
 #define GetMember(mem) \
         ((v = rb_struct_getmember(dqb,rb_intern(mem))) == Qnil) ? 0 : (NUM2UINT(v))
+#endif
 #if defined(USE_LINUX_QUOTA)
   c_dqb->dqb_bhardlimit = GetMember("bhardlimit");
   c_dqb->dqb_bsoftlimit = GetMember("bsoftlimit");
-#if !defined(USE_LINUX_QUOTA_24)
+#if defined(USE_LINUX_QUOTA_24) || defined(USE_LINUX_QUOTA_26)
+  c_dqb->dqb_curspace  = GetMember("curspace");
+#else
   c_dqb->dqb_curblocks  = GetMember("curblocks");
 #endif
   c_dqb->dqb_ihardlimit = GetMember("ihardlimit");
@@ -271,20 +504,29 @@ rb_diskquota_new(struct dqblk *c_dqb)
   VALUE dqb;
 
 #if defined(USE_LINUX_QUOTA)
+#if defined(USE_LINUX_QUOTA_24) || defined(USE_LINUX_QUOTA_26)
+  dqb = rb_struct_new(rb_sDiskQuota,
+		      ULL2NUM(c_dqb->dqb_bhardlimit),
+		      ULL2NUM(c_dqb->dqb_bsoftlimit),
+		      ULL2NUM(c_dqb->dqb_curspace),
+		      ULL2NUM(c_dqb->dqb_ihardlimit),
+		      ULL2NUM(c_dqb->dqb_isoftlimit),
+		      ULL2NUM(c_dqb->dqb_curinodes),
+		      ULL2NUM(c_dqb->dqb_btime),
+		      ULL2NUM(c_dqb->dqb_itime),
+		      0);
+#else
   dqb = rb_struct_new(rb_sDiskQuota,
 		      UINT2NUM(c_dqb->dqb_bhardlimit),
 		      UINT2NUM(c_dqb->dqb_bsoftlimit),
-#if defined(USE_LINUX_QUOTA_24)
-		      UINT2NUM(c_dqb->dqb_curspace),
-#else
 		      UINT2NUM(c_dqb->dqb_curblocks),
-#endif
 		      UINT2NUM(c_dqb->dqb_ihardlimit),
 		      UINT2NUM(c_dqb->dqb_isoftlimit),
 		      UINT2NUM(c_dqb->dqb_curinodes),
 		      UINT2NUM(c_dqb->dqb_btime),
 		      UINT2NUM(c_dqb->dqb_itime),
 		      0);
+#endif
 #elif defined(USE_BSD_QUOTA)
   dqb = rb_struct_new(rb_sDiskQuota,
 		      UINT2NUM(c_dqb->dqb_bhardlimit),
@@ -296,7 +538,7 @@ rb_diskquota_new(struct dqblk *c_dqb)
 		      UINT2NUM(c_dqb->dqb_btime),
 		      UINT2NUM(c_dqb->dqb_itime),
 		      0);
-#elif defined(USE_SOLARIS)
+#elif defined(USE_SOLARIS_QUOTA)
   dqb = rb_struct_new(rb_sDiskQuota,
 		      UINT2NUM(c_dqb->dqb_bhardlimit),
 		      UINT2NUM(c_dqb->dqb_bsoftlimit),
@@ -311,18 +553,34 @@ rb_diskquota_new(struct dqblk *c_dqb)
   return dqb;
 };
 
+#endif /* USE_NETBSD_QUOTA */
+
 static VALUE
 rb_quota_getquota(VALUE self, VALUE dev, VALUE uid)
 {
-  char *c_dev = STR2CSTR(dev);
+  char *c_dev = StringValuePtr(dev);
+#ifdef USE_NETBSD_QUOTA
+  struct quotaval c_blocks, c_files;
+#else
   struct dqblk c_dqb;
+#endif
   VALUE dqb = Qnil;
 
+#ifdef USE_NETBSD_QUOTA
+  if( rb_quotaget(c_dev, uid, &c_blocks, &c_files) == -1 ){
+    rb_sys_fail("quota_get");
+  };
+#else
   if( rb_quotactl(Q_GETQUOTA,c_dev,uid,(caddr_t)(&c_dqb)) == -1 ){
     rb_sys_fail("quotactl");
   };
+#endif
 
+#ifdef USE_NETBSD_QUOTA
+  dqb = rb_diskquota_new(&c_blocks, &c_files);
+#else
   dqb = rb_diskquota_new(&c_dqb);
+#endif
 
   return dqb;
 };
@@ -330,11 +588,17 @@ rb_quota_getquota(VALUE self, VALUE dev,
 VALUE
 rb_quota_quotaoff(VALUE self, VALUE dev)
 {
-  char *c_dev = STR2CSTR(dev);
+  char *c_dev = StringValuePtr(dev);
 
+#ifdef USE_NETBSD_QUOTA
+  if( rb_quotaoff(c_dev) == -1 ){
+    rb_sys_fail("quota_quotaoff");
+  };
+#else
   if( rb_quotactl(Q_QUOTAOFF,c_dev,Qnil,NULL) == -1 ){
     rb_sys_fail("quotactl");
   };
+#endif
 
   return Qnil;
 };
@@ -342,12 +606,20 @@ rb_quota_quotaoff(VALUE self, VALUE dev)
 VALUE
 rb_quota_quotaon(VALUE self, VALUE dev, VALUE quotas)
 {
-  char *c_dev = STR2CSTR(dev);
-  char *c_quotas = STR2CSTR(quotas);
+  char *c_dev = StringValuePtr(dev);
+  char *c_quotas = StringValuePtr(quotas);
 
+#ifdef USE_NETBSD_QUOTA
+  /* ignore the quota file names - they must be placed in /etc/fstab */
+  (void)quotas;
+  if( rb_quotaon(c_dev) == -1 ){
+    rb_sys_fail("quota_quotaon");
+  };
+#else
   if( rb_quotactl(Q_QUOTAON,c_dev,Qnil,(caddr_t)c_quotas) == -1 ){
     rb_sys_fail("quotactl");
   };
+#endif
 
   return Qnil;
 };
@@ -355,14 +627,28 @@ rb_quota_quotaon(VALUE self, VALUE dev, 
 VALUE
 rb_quota_setquota(VALUE self, VALUE dev, VALUE uid, VALUE dqb)
 {
-  char *c_dev = STR2CSTR(dev);
+  char *c_dev = StringValuePtr(dev);
+#ifdef USE_NETBSD_QUOTA
+  struct quotaval c_qvb, c_qvf;
+#else
   struct dqblk c_dqb;
+#endif
 
+#ifdef USE_NETBSD_QUOTA
+  rb_diskquota_get(dqb, &c_qvb, &c_qvf);
+#else
   rb_diskquota_get(dqb, &c_dqb);
+#endif
 
+#ifdef USE_NETBSD_QUOTA
+  if( rb_quotaput(c_dev,uid,&c_qvb, &c_qvf) == -1 ){
+    rb_sys_fail("quota_put");
+  };
+#else
   if( rb_quotactl(Q_SETQUOTA,c_dev,uid,(caddr_t)(&c_dqb)) == -1 ){
     rb_sys_fail("quotactl");
   };
+#endif
 
   return Qnil;
 };
@@ -371,7 +657,7 @@ VALUE
 rb_quota_setqlim(VALUE self, VALUE dev, VALUE uid, VALUE dqb)
 {
 #ifdef Q_SETQLIM
-  char *c_dev = STR2CSTR(dev);
+  char *c_dev = StringValuePtr(dev);
   struct dqblk c_dqb;
 
   rb_diskquota_get(dqb, &c_dqb);
@@ -392,18 +678,22 @@ rb_quota_setqlim(VALUE self, VALUE dev, 
 VALUE
 rb_quota_sync(VALUE self, VALUE dev)
 {
+#ifdef USE_NETBSD_QUOTA
+  /* nothing */
+#else
   char *c_dev;
 
   if( dev == Qnil ){
     c_dev = NULL;
   }
   else{
-    c_dev = STR2CSTR(dev);
+    c_dev = StringValuePtr(dev);
   };
 
   if( rb_quotactl(Q_SYNC,c_dev,Qnil,NULL) == -1 ){ /* uid and addr are ignored */
     rb_sys_fail("quotactl");
   };
+#endif /* USE_NETBSD_QUOTA */
 
   return Qnil;
 };
@@ -442,7 +732,7 @@ Init_quota()
 				   "curinodes",
 				   "btimelimit",
 				   "itimelimit",
-				   0);
+				   NULL);
 
   /* for compatibility */
 #define DQ_ALIAS(a,b) rb_alias(rb_sDiskQuota,rb_intern(#a),rb_intern(#b))
@@ -454,7 +744,7 @@ Init_quota()
   DQ_ALIAS(fsoftlimit=, isoftlimit=);
   DQ_ALIAS(curfiles=,   curinodes=);
   DQ_ALIAS(ftimelimit=, itimelimit=);
-#if defined(USE_LINUX_QUOTA_24)
+#if defined(USE_LINUX_QUOTA_24) || defined(USE_LINUX_QUOTA_26)
   DQ_ALIAS(curspace, curblocks);
   DQ_ALIAS(curspace=, curblocks=);
 #endif
