r1257: Ensure we deferr a sharing violation on rename correctly.
[ira/wip.git] / source3 / smbd / quotas.c
index 5b843bd09a6e996eb6bd89a6bdf205ccd5a21e33..e439c1e571a76067aa0a9ea62b780aa5ec40ee07 100644 (file)
 
 #include "includes.h"
 
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_QUOTA
+
+#ifndef HAVE_SYS_QUOTAS
+
+/* just a quick hack because sysquotas.h is included before linux/quota.h */
+#ifdef QUOTABLOCK_SIZE
+#undef QUOTABLOCK_SIZE
+#endif
+
+#ifdef WITH_QUOTAS
+
 #if defined(VXFS_QUOTA)
 
 /*
@@ -41,33 +53,16 @@ BOOL disk_quotas_vxfs(const pstring name, char *path, SMB_BIG_UINT *bsize, SMB_B
 #ifdef LINUX
 
 #include <sys/types.h>
-#include <asm/types.h>
+#include <mntent.h>
 
 /*
  * This shouldn't be neccessary - it should be /usr/include/sys/quota.h
- * Unfortunately, RH7.1 ships with a different quota system using struct mem_dqblk
- * rather than the struct dqblk defined in /usr/include/sys/quota.h.
- * This means we must include linux/quota.h to have a hope of working on
- * RH7.1 systems. And it also means this breaks if the kernel is upgraded
- * to a Linus 2.4.x (where x > the minor number shipped with RH7.1) until
- * Linus synchronises with the AC patches. Sometimes I *hate* Linux :-). JRA.
+ * So we include all the files has *should* be in the system into a large,
+ * grungy samba_linux_quoatas.h Sometimes I *hate* Linux :-). JRA.
  */
 
-#include <linux/quota.h>
-#ifdef HAVE_LINUX_XQM_H
-#include <linux/xqm.h>
-#else
-#ifdef HAVE_XFS_XQM_H
-#include <xfs/xqm.h>
-#define HAVE_LINUX_XQM_H
-#endif
-#endif
-
-#include <mntent.h>
-#include <linux/unistd.h>
-
-
-#define LINUX_QUOTAS_2
+#include "samba_linux_quota.h"
+#include "samba_xfs_quota.h"
 
 typedef struct _LINUX_SMB_DISK_QUOTA {
        SMB_BIG_UINT bsize;
@@ -83,67 +78,120 @@ typedef struct _LINUX_SMB_DISK_QUOTA {
  Abstract out the XFS Quota Manager quota get call.
 ****************************************************************************/
 
-static int get_smb_linux_xfs_quota(char *path, uid_t euser_id, LINUX_SMB_DISK_QUOTA *dp)
+static int get_smb_linux_xfs_quota(char *path, uid_t euser_id, gid_t egrp_id, LINUX_SMB_DISK_QUOTA *dp)
 {
-       int ret = -1;
-#ifdef HAVE_LINUX_XQM_H
-       struct fs_disk_quota D;
-       ZERO_STRUCT(D);
-
-       if ((ret = quotactl(QCMD(Q_XGETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D)))
-               return ret;
-
-       dp->bsize = (SMB_BIG_UINT)512;
-       dp->softlimit = (SMB_BIG_UINT)D.d_blk_softlimit;
-       dp->hardlimit = (SMB_BIG_UINT)D.d_blk_hardlimit;
-       dp->ihardlimit = (SMB_BIG_UINT)D.d_ino_hardlimit;
-       dp->isoftlimit = (SMB_BIG_UINT)D.d_ino_softlimit;
-       dp->curinodes = (SMB_BIG_UINT)D.d_icount;
-       dp->curblocks = (SMB_BIG_UINT)D.d_bcount;
-#endif
-       return ret;
+       struct fs_disk_quota D;
+       int ret;
+
+       ZERO_STRUCT(D);
+
+       ret = quotactl(QCMD(Q_XGETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D);
+
+       if (ret)
+               ret = quotactl(QCMD(Q_XGETQUOTA,GRPQUOTA), path, egrp_id, (caddr_t)&D);
+
+       if (ret)
+               return ret;
+
+       dp->bsize = (SMB_BIG_UINT)512;
+       dp->softlimit = (SMB_BIG_UINT)D.d_blk_softlimit;
+       dp->hardlimit = (SMB_BIG_UINT)D.d_blk_hardlimit;
+       dp->ihardlimit = (SMB_BIG_UINT)D.d_ino_hardlimit;
+       dp->isoftlimit = (SMB_BIG_UINT)D.d_ino_softlimit;
+       dp->curinodes = (SMB_BIG_UINT)D.d_icount;
+       dp->curblocks = (SMB_BIG_UINT)D.d_bcount;
+
+       return ret;
 }
 
 /****************************************************************************
  Abstract out the old and new Linux quota get calls.
 ****************************************************************************/
 
-static int get_smb_linux_vfs_quota(char *path, uid_t euser_id, LINUX_SMB_DISK_QUOTA *dp)
+static int get_smb_linux_v1_quota(char *path, uid_t euser_id, gid_t egrp_id, LINUX_SMB_DISK_QUOTA *dp)
 {
+       struct v1_kern_dqblk D;
        int ret;
-#ifdef LINUX_QUOTAS_1
-       struct dqblk D;
-       ZERO_STRUCT(D);
-       dp->bsize = (SMB_BIG_UINT)1024;
-#else /* LINUX_QUOTAS_2 */
-       struct mem_dqblk D;
+
        ZERO_STRUCT(D);
-#ifndef QUOTABLOCK_SIZE
-#define QUOTABLOCK_SIZE 1024
-#endif
+
+       ret = quotactl(QCMD(Q_V1_GETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D);
+
+       if (ret && errno != EDQUOT)
+               ret = quotactl(QCMD(Q_V1_GETQUOTA,GRPQUOTA), path, egrp_id, (caddr_t)&D);
+
+       if (ret && errno != EDQUOT)
+               return ret;
+
        dp->bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
-#endif
+       dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
+       dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
+       dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
+       dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
+       dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
+       dp->curblocks = (SMB_BIG_UINT)D.dqb_curblocks;
+
+       return ret;
+}
+
+static int get_smb_linux_v2_quota(char *path, uid_t euser_id, gid_t egrp_id, LINUX_SMB_DISK_QUOTA *dp)
+{
+       struct v2_kern_dqblk D;
+       int ret;
+
+       ZERO_STRUCT(D);
+
+       ret = quotactl(QCMD(Q_V2_GETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D);
+
+       if (ret && errno != EDQUOT)
+               ret = quotactl(QCMD(Q_V2_GETQUOTA,GRPQUOTA), path, egrp_id, (caddr_t)&D);
 
-       if ((ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D)))
-               return -1;
+       if (ret && errno != EDQUOT)
+               return ret;
 
+       dp->bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
        dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
        dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
        dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
        dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
        dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
+       dp->curblocks = ((SMB_BIG_UINT)D.dqb_curspace) / dp->bsize;
 
-#ifdef LINUX_QUOTAS_1
-       dp->curblocks = (SMB_BIG_UINT)D.dqb_curblocks;
-#else /* LINUX_QUOTAS_2 */
-       dp->curblocks = ((SMB_BIG_UINT)D.dqb_curspace)/ dp->bsize;
-#endif
+       return ret;
+}
+
+/****************************************************************************
+ Brand-new generic quota interface.
+****************************************************************************/
+
+static int get_smb_linux_gen_quota(char *path, uid_t euser_id, gid_t egrp_id, LINUX_SMB_DISK_QUOTA *dp)
+{
+       struct if_dqblk D;
+       int ret;
+
+       ZERO_STRUCT(D);
+
+       ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D);
+
+       if (ret && errno != EDQUOT)
+               ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), path, egrp_id, (caddr_t)&D);
+
+       if (ret && errno != EDQUOT)
+               return ret;
+
+       dp->bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE;
+       dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit;
+       dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit;
+       dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit;
+       dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit;
+       dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes;
+       dp->curblocks = ((SMB_BIG_UINT)D.dqb_curspace) / dp->bsize;
 
-       return 0;
+       return ret;
 }
 
 /****************************************************************************
-try to get the disk space from disk quotas (LINUX version)
+ Try to get the disk space from disk quotas (LINUX version).
 ****************************************************************************/
 
 BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
@@ -156,9 +204,11 @@ BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB
        SMB_DEV_T devno;
        int found;
        uid_t euser_id;
+       gid_t egrp_id;
 
        euser_id = geteuid();
-  
+       egrp_id = getegid();
+
        /* find the block device file */
   
        if ( sys_stat(path, &S) == -1 )
@@ -186,10 +236,18 @@ BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB
 
        save_re_uid();
        set_effective_uid(0);  
-       if (strcmp(mnt->mnt_type, "xfs") == 0)
-               r=get_smb_linux_xfs_quota(mnt->mnt_fsname, euser_id, &D);
-       else
-               r=get_smb_linux_vfs_quota(mnt->mnt_fsname, euser_id, &D);
+
+       if (strcmp(mnt->mnt_type, "xfs")==0) {
+               r=get_smb_linux_xfs_quota(mnt->mnt_fsname, euser_id, egrp_id, &D);
+       } else {
+               r=get_smb_linux_gen_quota(mnt->mnt_fsname, euser_id, egrp_id, &D);
+               if (r == -1 && errno != EDQUOT) {
+                       r=get_smb_linux_v2_quota(mnt->mnt_fsname, euser_id, egrp_id, &D);
+                       if (r == -1 && errno != EDQUOT)
+                               r=get_smb_linux_v1_quota(mnt->mnt_fsname, euser_id, egrp_id, &D);
+               }
+       }
+
        restore_re_uid();
 
        /* Use softlimit to determine disk space, except when it has been exceeded */
@@ -898,8 +956,8 @@ BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB
   if ((sys_stat(path, &S)<0) || (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 1)<0))
 #else
   if ((sys_stat(path, &S)<0) || (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 0)<0)) 
-#endif /* ifdef HPUX */
        return (False);
+#endif /* ifdef HPUX */
 
 #endif /* !defined(__FreeBSD__) && !defined(AIX) && !defined(__OpenBSD__) */
 
@@ -936,7 +994,11 @@ BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB
 #elif defined(AIX)
   /* AIX has both USER and GROUP quotas: 
      Get the USER quota (ohnielse@fysik.dtu.dk) */
+  save_re_uid();
+  if (set_re_uid() != 0) 
+    return False;
   r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D);
+  restore_re_uid();
 #else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
   r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D);
 #endif /* !__FreeBSD__ && !AIX && !__OpenBSD__ */
@@ -1112,3 +1174,106 @@ BOOL disk_quotas_vxfs(const pstring name, char *path, SMB_BIG_UINT *bsize, SMB_B
 #endif /* SUNOS5 || ... */
 
 #endif /* VXFS_QUOTA */
+
+#else /* WITH_QUOTAS */
+
+BOOL disk_quotas(const char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
+{
+  (*bsize) = 512; /* This value should be ignored */
+
+  /* And just to be sure we set some values that hopefully */
+  /* will be larger that any possible real-world value     */
+  (*dfree) = (SMB_BIG_UINT)-1;
+  (*dsize) = (SMB_BIG_UINT)-1;
+
+  /* As we have select not to use quotas, allways fail */
+  return False;
+}
+#endif /* WITH_QUOTAS */
+
+#else /* HAVE_SYS_QUOTAS */
+/* wrapper to the new sys_quota interface 
+   this file should be removed later
+   */
+BOOL disk_quotas(const char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize)
+{
+       int r;
+       SMB_DISK_QUOTA D;
+       unid_t id;
+
+       id.uid = geteuid();
+
+       ZERO_STRUCT(D);
+       r=sys_get_quota(path, SMB_USER_QUOTA_TYPE, id, &D);
+
+       /* Use softlimit to determine disk space, except when it has been exceeded */
+       *bsize = D.bsize;
+       if (r == -1) {
+               if (errno == EDQUOT) {
+                       *dfree =0;
+                       *dsize =D.curblocks;
+                       return (True);
+               } else {
+                       goto try_group_quota;
+               }
+       }
+
+       /* Use softlimit to determine disk space, except when it has been exceeded */
+       if (
+               (D.softlimit && D.curblocks >= D.softlimit) ||
+               (D.hardlimit && D.curblocks >= D.hardlimit) ||
+               (D.isoftlimit && D.curinodes >= D.isoftlimit) ||
+               (D.ihardlimit && D.curinodes>=D.ihardlimit)
+       ) {
+               *dfree = 0;
+               *dsize = D.curblocks;
+       } else if (D.softlimit==0 && D.hardlimit==0) {
+               goto try_group_quota;
+       } else {
+               if (D.softlimit == 0)
+                       D.softlimit = D.hardlimit;
+               *dfree = D.softlimit - D.curblocks;
+               *dsize = D.softlimit;
+       }
+
+       return True;
+       
+try_group_quota:
+       id.gid = getegid();
+
+       ZERO_STRUCT(D);
+       r=sys_get_quota(path, SMB_GROUP_QUOTA_TYPE, id, &D);
+
+       /* Use softlimit to determine disk space, except when it has been exceeded */
+       *bsize = D.bsize;
+       if (r == -1) {
+               if (errno == EDQUOT) {
+                       *dfree =0;
+                       *dsize =D.curblocks;
+                       return (True);
+               } else {
+                       return False;
+               }
+       }
+
+       /* Use softlimit to determine disk space, except when it has been exceeded */
+       if (
+               (D.softlimit && D.curblocks >= D.softlimit) ||
+               (D.hardlimit && D.curblocks >= D.hardlimit) ||
+               (D.isoftlimit && D.curinodes >= D.isoftlimit) ||
+               (D.ihardlimit && D.curinodes>=D.ihardlimit)
+       ) {
+               *dfree = 0;
+               *dsize = D.curblocks;
+       } else if (D.softlimit==0 && D.hardlimit==0) {
+               return False;
+       } else {
+               if (D.softlimit == 0)
+                       D.softlimit = D.hardlimit;
+               *dfree = D.softlimit - D.curblocks;
+               *dsize = D.softlimit;
+       }
+
+       return (True);
+}
+#endif /* HAVE_SYS_QUOTAS */