[PATCHESv3]vfs: separate fs stats from quota in vfs modules

Uri Simchoni uri at samba.org
Tue Jan 19 22:13:58 UTC 2016


Thanks for looking into this!
Attached is v3 of the patchset. Relative to v2:
- Patch 1/15 fixed if{} style in loadparm change
- Patch 4/15 - make the module build only in developer mode, and fix 
True->true, False->false

Thanks,
Uri.

On 01/19/2016 10:46 PM, Volker Lendecke wrote:
> On Mon, Jan 18, 2016 at 11:15:56PM +0200, Uri Simchoni wrote:
>> Hi,
>> Attached is V2 of the refactoring. Changes were made only to the
>> mock module (As per Volker's suggestion - to use smb.conf and
>> lp_parm_XX() for mock configuration rather than a custom config file
>> and custom config code), and conversely to the test framework (but
>> not to the tests themselves). The mock module is greatly simplified
>> by this change.
>>
>> The building of the tests is also squashed now to fewer commits.
> This looks really good now. Only very few cosmetic comments: In the module
> there's a few if's without {}. Also, right now we prefer "False->false",
> "(True)->true". Last one: What about building this only in developer mode?
>
> Volker
>

-------------- next part --------------
>From 8310eb499e9baaaf00b04b0e45829a868caf8f54 Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Mon, 18 Jan 2016 06:56:43 +0200
Subject: [PATCH v3 01/15] loadparm: introduce lp_parm_ulonglong() and
 lpcfg_parm_ulonglong()

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 lib/param/loadparm.c     | 33 +++++++++++++++++++++++++++++++++
 lib/param/param.h        |  4 ++++
 source3/include/proto.h  |  3 +++
 source3/param/loadparm.c | 16 ++++++++++++++++
 4 files changed, 56 insertions(+)

diff --git a/lib/param/loadparm.c b/lib/param/loadparm.c
index 9b36c7e..9a3451f 100644
--- a/lib/param/loadparm.c
+++ b/lib/param/loadparm.c
@@ -329,6 +329,20 @@ unsigned long lp_ulong(const char *s)
 }
 
 /**
+ * convenience routine to return unsigned long long parameters.
+ */
+unsigned long long lp_ulonglong(const char *s)
+{
+
+	if (!s || !*s) {
+		DEBUG(0, ("lp_ulonglong(%s): is called with NULL!\n", s));
+		return -1;
+	}
+
+	return strtoull(s, NULL, 0);
+}
+
+/**
  * convenience routine to return unsigned long parameters.
  */
 static long lp_long(const char *s)
@@ -473,6 +487,25 @@ unsigned long lpcfg_parm_ulong(struct loadparm_context *lp_ctx,
 	return default_v;
 }
 
+/**
+ * Return parametric option from a given service.
+ * Type is a part of option before ':'
+ * Parametric option has following syntax: 'Type: option = value'
+ */
+unsigned long long lpcfg_parm_ulonglong(struct loadparm_context *lp_ctx,
+					struct loadparm_service *service,
+					const char *type, const char *option,
+					unsigned long long default_v)
+{
+	const char *value = lpcfg_get_parametric(lp_ctx, service, type, option);
+
+	if (value) {
+		return lp_ulonglong(value);
+	}
+
+	return default_v;
+}
+
 long lpcfg_parm_long(struct loadparm_context *lp_ctx,
 		     struct loadparm_service *service, const char *type,
 		     const char *option, long default_v)
diff --git a/lib/param/param.h b/lib/param/param.h
index 500f52d..fb28218 100644
--- a/lib/param/param.h
+++ b/lib/param/param.h
@@ -98,6 +98,10 @@ int lpcfg_parm_bytes(struct loadparm_context *lp_ctx,
 unsigned long lpcfg_parm_ulong(struct loadparm_context *lp_ctx,
 			    struct loadparm_service *service, const char *type,
 			    const char *option, unsigned long default_v);
+unsigned long long lpcfg_parm_ulonglong(struct loadparm_context *lp_ctx,
+					struct loadparm_service *service,
+					const char *type, const char *option,
+					unsigned long long default_v);
 long lpcfg_parm_long(struct loadparm_context *lp_ctx,
 		     struct loadparm_service *service, const char *type,
 		     const char *option, long default_v);
diff --git a/source3/include/proto.h b/source3/include/proto.h
index 809cb95..5a42010 100644
--- a/source3/include/proto.h
+++ b/source3/include/proto.h
@@ -965,6 +965,9 @@ const char *lp_parm_const_string_service(struct loadparm_service *service, const
 const char **lp_parm_string_list(int snum, const char *type, const char *option, const char **def);
 int lp_parm_int(int snum, const char *type, const char *option, int def);
 unsigned long lp_parm_ulong(int snum, const char *type, const char *option, unsigned long def);
+unsigned long long lp_parm_ulonglong(int snum, const char *type,
+				     const char *option,
+				     unsigned long long def);
 bool lp_parm_bool(int snum, const char *type, const char *option, bool def);
 struct enum_list;
 int lp_parm_enum(int snum, const char *type, const char *option,
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index 9f4a2b4..4fefa53 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -1228,6 +1228,22 @@ unsigned long lp_parm_ulong(int snum, const char *type, const char *option, unsi
 /* Return parametric option from a given service. Type is a part of option before ':' */
 /* Parametric option has following syntax: 'Type: option = value' */
 
+unsigned long long lp_parm_ulonglong(int snum, const char *type,
+				     const char *option, unsigned long long def)
+{
+	struct parmlist_entry *data = get_parametrics(snum, type, option);
+
+	if (data && data->value && *data->value) {
+		return lp_ulonglong(data->value);
+	}
+
+	return def;
+}
+
+/* Return parametric option from a given service. Type is a part of option
+ * before ':' */
+/* Parametric option has following syntax: 'Type: option = value' */
+
 bool lp_parm_bool(int snum, const char *type, const char *option, bool def)
 {
 	struct parmlist_entry *data = get_parametrics(snum, type, option);
-- 
2.4.3


>From 7125ae9ff267c1a7414d875447045084bdac3f2e Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Wed, 6 Jan 2016 13:30:59 +0200
Subject: [PATCH v3 02/15] quotas: correct comment about SMB_GROUP_QUOTA_TYPE

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 source3/include/ntquotas.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source3/include/ntquotas.h b/source3/include/ntquotas.h
index d8a0dc3..6fbbbb9 100644
--- a/source3/include/ntquotas.h
+++ b/source3/include/ntquotas.h
@@ -63,7 +63,7 @@ enum SMB_QUOTA_TYPE {
 	SMB_USER_FS_QUOTA_TYPE = 1,
 	SMB_USER_QUOTA_TYPE = 2,
 	SMB_GROUP_FS_QUOTA_TYPE = 3,/* not used yet */
-	SMB_GROUP_QUOTA_TYPE = 4 /* not in use yet, maybe for disk_free queries */
+	SMB_GROUP_QUOTA_TYPE = 4 /* used by disk_free queries */
 };
 
 typedef struct _SMB_NTQUOTA_STRUCT {
-- 
2.4.3


>From 7362988b09cf0c5385699aa52cc8764810e9fb84 Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Sat, 9 Jan 2016 21:24:10 +0200
Subject: [PATCH v3 03/15] smbd: enable unit-testing of
 NT_TRANSACT_GET_USER_QUOTA

Processing of NT_TRANSACT_GET_USER_QUOTA involves a security check to
see the user is an admin, allow this check to run in unit-testing mode.

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 source3/smbd/nttrans.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c
index b5fbbfd..75b283e 100644
--- a/source3/smbd/nttrans.c
+++ b/source3/smbd/nttrans.c
@@ -2310,7 +2310,7 @@ static void call_nt_transact_get_user_quota(connection_struct *conn,
 	ZERO_STRUCT(qt);
 
 	/* access check */
-	if (get_current_uid(conn) != 0) {
+	if (get_current_uid(conn) != sec_initial_uid()) {
 		DEBUG(1,("get_user_quota: access_denied service [%s] user "
 			 "[%s]\n", lp_servicename(talloc_tos(), SNUM(conn)),
 			 conn->session_info->unix_info->unix_name));
-- 
2.4.3


>From ed9622cd1a87d4152f776d2bd19c632ea8a26378 Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Tue, 5 Jan 2016 21:12:00 +0200
Subject: [PATCH v3 04/15] vfs_fake_dfq: add vfs module

Add a vfs module "vfs_fake_dfq" for mocking disk-free
and user/group quota functions.

In this commit there are quota considerations in
disk_free calculations, based on the mocking of quota.

Those considerations will later be removed once we
refactor the server code to weigh the disk-free and quota in
the smb layer and not in individual vfs implementations.

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 source3/modules/vfs_fake_dfq.c | 275 +++++++++++++++++++++++++++++++++++++++++
 source3/modules/wscript_build  |   7 ++
 source3/wscript                |   1 +
 3 files changed, 283 insertions(+)
 create mode 100644 source3/modules/vfs_fake_dfq.c

diff --git a/source3/modules/vfs_fake_dfq.c b/source3/modules/vfs_fake_dfq.c
new file mode 100644
index 0000000..ca8a2fc
--- /dev/null
+++ b/source3/modules/vfs_fake_dfq.c
@@ -0,0 +1,275 @@
+/*
+ * Fake Disk-Free and Quota VFS module.  Implements passthrough operation
+ * of all VFS calls, except for "disk free" and "get quota" which
+ * are handled by reading a text file named ".dfq" in the current directory.
+ *
+ * This module is intended for testing purposes.
+ *
+ * Copyright (C) Uri Simchoni, 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "smbd/smbd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+static int dfq_get_quota_do(struct vfs_handle_struct *handle, const char *path,
+			    enum SMB_QUOTA_TYPE qtype, unid_t id,
+			    SMB_DISK_QUOTA *qt);
+
+static uint64_t dfq_load_param(int snum, const char *path, const char *section,
+			       const char *param, uint64_t def_val)
+{
+	uint64_t ret;
+
+	char *option =
+	    talloc_asprintf(talloc_tos(), "%s/%s/%s", section, param, path);
+	if (option == NULL) {
+		return def_val;
+	}
+
+	ret = (uint64_t)lp_parm_ulonglong(snum, "fake_dfq", option,
+					  (unsigned long long)def_val);
+
+	TALLOC_FREE(option);
+
+	return ret;
+}
+
+static bool dfq_disk_quotas(vfs_handle_struct *handle, const char *path,
+			    uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
+{
+	int r;
+	SMB_DISK_QUOTA D;
+	unid_t id;
+
+	id.uid = geteuid();
+
+	ZERO_STRUCT(D);
+	r = dfq_get_quota_do(handle, 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 = dfq_get_quota_do(handle, 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);
+}
+
+static uint64_t dfq_disk_free(vfs_handle_struct *handle, const char *path,
+			      uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
+{
+	uint64_t free_1k;
+	uint64_t dfree_q = 0;
+	uint64_t bsize_q = 0;
+	uint64_t dsize_q = 0;
+	int snum = SNUM(handle->conn);
+	uint64_t dfq_bsize = 0;
+	char *rpath = NULL;
+
+	/* look up the params based on real path to be resilient
+	 * to refactoring of path<->realpath
+	 */
+	rpath = SMB_VFS_NEXT_REALPATH(handle, path);
+	if (rpath != NULL) {
+		dfq_bsize = dfq_load_param(snum, rpath, "df", "block size", 0);
+	}
+	if (dfq_bsize == 0) {
+		SAFE_FREE(rpath);
+		return SMB_VFS_NEXT_DISK_FREE(handle, path, bsize, dfree,
+					      dsize);
+	}
+
+	*bsize = dfq_bsize;
+	*dfree = dfq_load_param(snum, rpath, "df", "disk free", 0);
+	*dsize = dfq_load_param(snum, rpath, "df", "disk size", 0);
+
+	if (dfq_disk_quotas(handle, path, &bsize_q, &dfree_q, &dsize_q)) {
+		(*bsize) = bsize_q;
+		(*dfree) = MIN(*dfree, dfree_q);
+		(*dsize) = MIN(*dsize, dsize_q);
+	}
+
+	disk_norm(bsize, dfree, dsize);
+
+	if ((*bsize) < 1024) {
+		free_1k = (*dfree) / (1024 / (*bsize));
+	} else {
+		free_1k = ((*bsize) / 1024) * (*dfree);
+	}
+
+	SAFE_FREE(rpath);
+	return free_1k;
+}
+
+static int dfq_get_quota_do(struct vfs_handle_struct *handle, const char *path,
+			    enum SMB_QUOTA_TYPE qtype, unid_t id,
+			    SMB_DISK_QUOTA *qt)
+{
+	int rc = 0;
+	int save_errno;
+	char *section = NULL;
+	int snum = SNUM(handle->conn);
+	uint64_t bsize = 0;
+	char *rpath = NULL;
+
+	rpath = SMB_VFS_NEXT_REALPATH(handle, path);
+	if (rpath == NULL) {
+		goto dflt;
+	}
+
+	switch (qtype) {
+	case SMB_USER_QUOTA_TYPE:
+		section = talloc_asprintf(talloc_tos(), "u%llu",
+					  (unsigned long long)id.uid);
+		break;
+	case SMB_GROUP_QUOTA_TYPE:
+		section = talloc_asprintf(talloc_tos(), "g%llu",
+					  (unsigned long long)id.gid);
+		break;
+	default:
+		break;
+	}
+
+	if (section == NULL) {
+		goto dflt;
+	}
+
+	bsize = dfq_load_param(snum, rpath, section, "block size", 0);
+	if (bsize == 0) {
+		goto dflt;
+	}
+
+	if (dfq_load_param(snum, rpath, section, "err", 0) != 0) {
+		errno = ENOTSUP;
+		rc = -1;
+		goto out;
+	}
+
+	ZERO_STRUCTP(qt);
+
+	qt->bsize = bsize;
+	qt->hardlimit = dfq_load_param(snum, rpath, section, "hard limit", 0);
+	qt->softlimit = dfq_load_param(snum, rpath, section, "soft limit", 0);
+	qt->curblocks = dfq_load_param(snum, rpath, section, "cur blocks", 0);
+	qt->ihardlimit =
+	    dfq_load_param(snum, rpath, section, "inode hard limit", 0);
+	qt->isoftlimit =
+	    dfq_load_param(snum, rpath, section, "inode soft limit", 0);
+	qt->curinodes = dfq_load_param(snum, rpath, section, "cur inodes", 0);
+
+	if (dfq_load_param(snum, rpath, section, "edquot", 0) != 0) {
+		errno = EDQUOT;
+		rc = -1;
+	}
+
+	goto out;
+
+dflt:
+	rc = SMB_VFS_NEXT_GET_QUOTA(handle, qtype, id, qt);
+
+out:
+	save_errno = errno;
+	TALLOC_FREE(section);
+	SAFE_FREE(rpath);
+	errno = save_errno;
+	return rc;
+}
+
+static int dfq_get_quota(struct vfs_handle_struct *handle,
+			 enum SMB_QUOTA_TYPE qtype, unid_t id,
+			 SMB_DISK_QUOTA *qt)
+{
+	return dfq_get_quota_do(handle, handle->conn->connectpath, qtype, id,
+				qt);
+}
+
+struct vfs_fn_pointers vfs_fake_dfq_fns = {
+    /* Disk operations */
+
+    .disk_free_fn = dfq_disk_free,
+    .get_quota_fn = dfq_get_quota,
+};
+
+NTSTATUS vfs_fake_dfq_init(void)
+{
+	return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fake_dfq",
+				&vfs_fake_dfq_fns);
+}
diff --git a/source3/modules/wscript_build b/source3/modules/wscript_build
index 4dc6653..77b28f6 100644
--- a/source3/modules/wscript_build
+++ b/source3/modules/wscript_build
@@ -490,3 +490,10 @@ bld.SAMBA3_MODULE('vfs_offline',
                  init_function='',
                  internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_offline'),
                  enabled=bld.SAMBA3_IS_ENABLED_MODULE('vfs_offline'))
+
+bld.SAMBA3_MODULE('vfs_fake_dfq',
+                 subsystem='vfs',
+                 source='vfs_fake_dfq.c',
+                 init_function='',
+                 internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_fake_dfq'),
+                 enabled=bld.SAMBA3_IS_ENABLED_MODULE('vfs_fake_dfq'))
diff --git a/source3/wscript b/source3/wscript
index 9b5a228..d9e10c0 100644
--- a/source3/wscript
+++ b/source3/wscript
@@ -1588,6 +1588,7 @@ main() {
         default_shared_modules.extend(TO_LIST('perfcount_test'))
         default_shared_modules.extend(TO_LIST('vfs_skel_opaque vfs_skel_transparent vfs_shadow_copy_test'))
         default_shared_modules.extend(TO_LIST('auth_skel pdb_test'))
+        default_shared_modules.extend(TO_LIST('vfs_fake_dfq'))
 
     if Options.options.enable_selftest or Options.options.developer:
         default_shared_modules.extend(TO_LIST('vfs_fake_acls vfs_nfs4acl_xattr'))
-- 
2.4.3


>From f4ac1d37c2a6f51d4f50b19fe4ff4dff3d52d097 Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Wed, 6 Jan 2016 12:59:06 +0200
Subject: [PATCH v3 05/15] selftest: add disk-free and quota tests based on
 fake_dfq VFS module

These tests use the vfs_fake_dfq module to simulate some
relations between the quota status and disk-free status.

The tests will become meaningful when we take the code that
does those calculations out of the VFS layer - the tests will
then exercise the server code.

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 selftest/selftesthelpers.py              |   1 +
 selftest/target/Samba3.pm                |  12 +++
 source3/script/tests/test_dfree_quota.sh | 174 +++++++++++++++++++++++++++++++
 source3/selftest/tests.py                |   1 +
 4 files changed, 188 insertions(+)
 create mode 100755 source3/script/tests/test_dfree_quota.sh

diff --git a/selftest/selftesthelpers.py b/selftest/selftesthelpers.py
index 0dfcf54..a5ab3f9 100644
--- a/selftest/selftesthelpers.py
+++ b/selftest/selftesthelpers.py
@@ -183,3 +183,4 @@ scriptdir = os.path.join(srcdir(), "script/tests")
 wbinfo = binpath('wbinfo')
 dbwrap_tool = binpath('dbwrap_tool')
 vfstest = binpath('vfstest')
+smbcquotas = binpath('smbcquotas')
diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index cd293a6..2bae96f 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -1244,6 +1244,7 @@ sub provision($$$$$$$$)
 	symlink "$widelinks_shrdir", "$widelinks_shrdir/dot";
 
 	my $conffile="$libdir/server.conf";
+	my $dfqconffile="$libdir/dfq.conf";
 
 	my $nss_wrapper_pl = "$ENV{PERL} $self->{srcdir}/lib/nss_wrapper/nss_wrapper.pl";
 	my $nss_wrapper_passwd = "$privatedir/passwd";
@@ -1622,9 +1623,20 @@ sub provision($$$$$$$$)
 	vfs objects = shadow_copy2
 	shadow:mountpoint = $shadow_mntdir
 	wide links = yes
+[dfq]
+	path = $shrdir/dfree
+	vfs objects = fake_dfq
+	admin users = $unix_name
+	include = $dfqconffile
 	";
 	close(CONF);
 
+	unless (open(DFQCONF, ">$dfqconffile")) {
+	        warn("Unable to open $dfqconffile");
+		return undef;
+	}
+	close(DFQCONF);
+
 	##
 	## create a test account
 	##
diff --git a/source3/script/tests/test_dfree_quota.sh b/source3/script/tests/test_dfree_quota.sh
new file mode 100755
index 0000000..8ae63f9
--- /dev/null
+++ b/source3/script/tests/test_dfree_quota.sh
@@ -0,0 +1,174 @@
+#!/bin/sh
+#
+# Blackbox test for disk-free, quota, and their interaction
+#
+
+if [ $# -lt 6 ]; then
+cat <<EOF
+Usage: test_dfree_quota.sh SERVER DOMAIN USERNAME PASSWORD LOCAL_PATH SMBCLIENT SMBCQUOTAS
+EOF
+exit 1;
+fi
+
+SERVER=$1
+DOMAIN=$2
+USERNAME=$3
+PASSWORD=$4
+ENVDIR=`dirname $5`
+WORKDIR=$5/dfree
+smbclient=$6
+smbcquotas=$7
+shift 7
+failed=0
+
+CONFFILE=$ENVDIR/lib/dfq.conf
+
+incdir=`dirname $0`/../../../testprogs/blackbox
+. $incdir/subunit.sh
+
+sighup_smbd() {
+    kill -HUP -`cat $ENVDIR/pid/smbd.pid`
+}
+
+conf_lines() {
+    local uid
+    local gid
+    uid=$(id -u $USERNAME)
+    gid=$(id -g $USERNAME)
+cat <<ABC
+conf1:df:block size = 512:disk free = 10:disk size = 20
+conf2:df:block size = 1024:disk free = 10:disk size = 20
+conf3:df:block size = 4096:disk free = 750:disk size = 281474976710656
+confq1:u$uid:block size = 4096:hard limit = 750:soft limit = 1000:cur blocks = 10
+confdfq1:df:block size = 4096:disk free = 10:disk size = 20
+confdfq1:u$uid:block size = 4096:hard limit = 750:soft limit = 1000:cur blocks = 10
+confdfq2:df:block size = 4096:disk free = 10:disk size = 20
+confdfq2:u$uid:block size = 4096:hard limit = 40:soft limit = 40:cur blocks = 37
+confdfq3:df:block size = 4096:disk free = 10:disk size = 80
+confdfq3:u$uid:block size = 4096:hard limit = 40:soft limit = 40:cur blocks = 0
+confdfq4:df:block size = 4096:disk free = 10:disk size = 80
+confdfq4:u$uid:block size = 4096:hard limit = 40:soft limit = 40:cur blocks = 37
+edquot:df:block size = 4096:disk free = 10:disk size = 80
+edquot:u$uid:block size = 4096:hard limit = 40:soft limit = 40:cur blocks = 41:edquot = 1
+slimit:df:block size = 4096:disk free = 10:disk size = 80
+slimit:u$uid:block size = 4096:hard limit = 44:soft limit = 40:cur blocks = 42
+hlimit:df:block size = 4096:disk free = 10:disk size = 80
+hlimit:u$uid:block size = 4096:hard limit = 44:soft limit = 0:cur blocks = 45
+islimit:df:block size = 4096:disk free = 10:disk size = 80
+islimit:u$uid:block size = 4096:hard limit = 44:soft limit = 40:cur blocks = 37:inode soft limit = 30:inode hard limit = 35:cur inodes = 32 
+ihlimit:df:block size = 4096:disk free = 10:disk size = 80
+ihlimit:u$uid:block size = 4096:hard limit = 44:soft limit = 40:cur blocks = 37:inode soft limit = 0:inode hard limit = 35:cur inodes = 36
+trygrp1:df:block size = 4096:disk free = 10:disk size = 80
+trygrp1:u$uid:block size = 4096:hard limit = 40:soft limit = 40:cur blocks = 41:err = 1
+trygrp1:g$gid:block size = 4096:hard limit = 60:soft limit = 60:cur blocks = 55
+trygrp2:df:block size = 4096:disk free = 10:disk size = 80
+trygrp2:u$uid:block size = 4096:hard limit = 0:soft limit = 0:cur blocks = 41
+trygrp2:g$gid:block size = 4096:hard limit = 60:soft limit = 60:cur blocks = 56
+ABC
+}
+
+setup_1_conf() {
+    conf_name="$1"
+    subdir="$2"
+    absdir=`realpath $WORKDIR/$subdir`
+    conf_lines | sed -rn "s/^$conf_name:(.*)/\1/p" | tr ":" "\n" | \
+    awk  -F '=' -v atdir=$absdir 'NF==1 {section=$1} NF==2 {sub(/\s*$/, "", $1); printf "\tfake_dfq:%s/%s/%s =%s\n", section, $1, atdir, $2}'
+}
+
+setup_conf() {
+    rm $CONFFILE
+    touch $CONFFILE
+
+    until [ -z "$1" ]
+    do
+        setup_1_conf $1 $2 >> $CONFFILE
+        shift
+        shift
+    done
+    sighup_smbd
+    #let it load...
+    sleep .5
+}
+
+
+test_smbclient_dfree() {
+	name="$1"
+	dir="$2"
+    confs="$3"
+    expected="$4"
+	shift
+    shift
+    shift
+    shift
+    subunit_start_test "$name"
+    setup_conf $confs
+	output=$($VALGRIND $smbclient //$SERVER/dfq -c "cd $dir; l" $@ 2>&1)
+    status=$?
+    if [ "$status" = "0" ]; then
+		received=$(echo "$output" | awk '/blocks of size/ {print $1, $5, $6}')
+		if [ "$expected" = "$received" ]; then
+			subunit_pass_test "$name"
+		else
+			echo "$output" | subunit_fail_test "$name"
+		fi
+	else
+		echo "$output" | subunit_fail_test "$name"
+	fi
+	return $status
+}
+
+test_smbcquotas() {
+	name="$1"
+    conf="$2"
+    user="$3"
+    expected="$4"
+	shift
+    shift
+    shift
+    shift
+	subunit_start_test "$name"
+    setup_conf "$conf" "."
+	output=$($VALGRIND $smbcquotas //$SERVER/dfq $@ 2>/dev/null)
+	status=$?
+	if [ "$status" = "0" ]; then
+		received=$(echo "$output" | awk "/$SERVER\\\\$user/ {printf \"%s%s%s\", \$3, \$4, \$5}")
+		if [ "$expected" = "$received" ]; then
+			subunit_pass_test "$name"
+		else
+			echo "$output" | subunit_fail_test "$name"
+		fi
+	else
+		echo "$output" | subunit_fail_test "$name"
+	fi
+	return $status
+}
+
+#basic disk-free tests
+test_smbclient_dfree "Test dfree share root SMB3 no quota" "." "conf1 ." "10 1024. 5" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+test_smbclient_dfree "Test dfree subdir SMB3 no quota" "subdir1" "conf1 . conf2 subdir1" "20 1024. 10" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+test_smbclient_dfree "Test dfree subdir NT1 no quota" "subdir1" "conf1 . conf2 subdir1" "10 1024. 5" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=NT1 || failed=`expr $failed + 1`
+test_smbclient_dfree "Test large disk" "." "conf3 ." "1125899906842624 1024. 3000" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+#basic quota test (SMB1 only)
+test_smbcquotas "Test user quota" confq1 $USERNAME "40960/4096000/3072000" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=NT1 || failed=`expr $failed + 1`
+
+#quota limit > disk size, remaining quota > disk free
+test_smbclient_dfree "Test dfree share root df vs quota case 1" "." "confdfq1 ." "80 1024. 40" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+#quota limit > disk size, remaining quota < disk free
+test_smbclient_dfree "Test dfree share root df vs quota case 2" "." "confdfq2 ." "80 1024. 12" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+#quota limit < disk size, remaining quota > disk free
+test_smbclient_dfree "Test dfree share root df vs quota case 3" "." "confdfq3 ." "160 1024. 40" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+#quota limit < disk size, remaining quota < disk free
+test_smbclient_dfree "Test dfree share root df vs quota case 4" "." "confdfq4 ." "160 1024. 12" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+test_smbclient_dfree "Test dfree subdir df vs quota case 4" "subdir1" "confdfq4 subdir1" "160 1024. 12" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+
+#quota-->disk free special cases
+test_smbclient_dfree "Test quota->dfree edquot" "subdir1" "edquot subdir1" "164 1024. 0" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+test_smbclient_dfree "Test quota->dfree soft limit" "subdir1" "slimit subdir1" "168 1024. 0" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+test_smbclient_dfree "Test quota->dfree hard limit" "subdir1" "hlimit subdir1" "180 1024. 0" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+test_smbclient_dfree "Test quota->dfree inode soft limit" "subdir1" "islimit subdir1" "148 1024. 0" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+test_smbclient_dfree "Test quota->dfree inode hard limit" "subdir1" "ihlimit subdir1" "148 1024. 0" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+test_smbclient_dfree "Test quota->dfree err try group" "subdir1" "trygrp1 subdir1" "240 1024. 20" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+test_smbclient_dfree "Test quota->dfree no-quota try group" "subdir1" "trygrp2 subdir1" "240 1024. 16" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+
+setup_conf
+exit $failed
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index 25d20fe..42119cc 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -177,6 +177,7 @@ for env in ["nt4_dc"]:
 for env in ["fileserver"]:
     plantestsuite("samba3.blackbox.preserve_case (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_preserve_case.sh"), '$SERVER', '$DOMAIN', '$USERNAME', '$PASSWORD', '$PREFIX', smbclient3])
     plantestsuite("samba3.blackbox.dfree_command (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_dfree_command.sh"), '$SERVER', '$DOMAIN', '$USERNAME', '$PASSWORD', '$PREFIX', smbclient3])
+    plantestsuite("samba3.blackbox.dfree_quota (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_dfree_quota.sh"), '$SERVER', '$DOMAIN', '$USERNAME', '$PASSWORD', '$LOCAL_PATH', smbclient3, smbcquotas])
     plantestsuite("samba3.blackbox.valid_users (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_valid_users.sh"), '$SERVER', '$SERVER_IP', '$DOMAIN', '$USERNAME', '$PASSWORD', '$PREFIX', smbclient3])
     plantestsuite("samba3.blackbox.offline (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_offline.sh"), '$SERVER', '$SERVER_IP', '$DOMAIN', '$USERNAME', '$PASSWORD', '$LOCAL_PATH/offline', smbclient3])
     plantestsuite("samba3.blackbox.shadow_copy2 (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_shadow_copy.sh"), '$SERVER', '$SERVER_IP', '$DOMAIN', '$USERNAME', '$PASSWORD', '$LOCAL_PATH/shadow', smbclient3])
-- 
2.4.3


>From a4e80595b9216fe3cae6760a4335ca2ff35e97dd Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Sun, 10 Jan 2016 13:29:25 +0200
Subject: [PATCH v3 06/15] vfs: add path parameter to get_quota

Adding a path parameter would allow the VFS get_quota
function to be used for determining the quota/usage
when calculating size and free spacei.

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 examples/VFS/skel_opaque.c          |  5 +++--
 examples/VFS/skel_transparent.c     |  7 ++++---
 source3/include/vfs.h               |  6 ++++--
 source3/include/vfs_macros.h        |  8 ++++----
 source3/modules/vfs_ceph.c          |  4 +++-
 source3/modules/vfs_default.c       |  6 ++++--
 source3/modules/vfs_default_quota.c | 13 +++++++++----
 source3/modules/vfs_fake_dfq.c      | 26 +++++++++-----------------
 source3/modules/vfs_full_audit.c    |  8 ++++----
 source3/modules/vfs_time_audit.c    |  6 +++---
 source3/smbd/ntquotas.c             |  2 +-
 source3/smbd/vfs.c                  |  4 ++--
 12 files changed, 50 insertions(+), 45 deletions(-)

diff --git a/examples/VFS/skel_opaque.c b/examples/VFS/skel_opaque.c
index 29fe6f4..a4309d4 100644
--- a/examples/VFS/skel_opaque.c
+++ b/examples/VFS/skel_opaque.c
@@ -54,8 +54,9 @@ static uint64_t skel_disk_free(vfs_handle_struct *handle, const char *path,
 	return 0;
 }
 
-static int skel_get_quota(vfs_handle_struct *handle, enum SMB_QUOTA_TYPE qtype,
-			  unid_t id, SMB_DISK_QUOTA *dq)
+static int skel_get_quota(vfs_handle_struct *handle, const char *path,
+			  enum SMB_QUOTA_TYPE qtype, unid_t id,
+			  SMB_DISK_QUOTA *dq)
 {
 	errno = ENOSYS;
 	return -1;
diff --git a/examples/VFS/skel_transparent.c b/examples/VFS/skel_transparent.c
index c5a36a6..2d1ed79 100644
--- a/examples/VFS/skel_transparent.c
+++ b/examples/VFS/skel_transparent.c
@@ -55,10 +55,11 @@ static uint64_t skel_disk_free(vfs_handle_struct *handle, const char *path,
 	return SMB_VFS_NEXT_DISK_FREE(handle, path, bsize, dfree, dsize);
 }
 
-static int skel_get_quota(vfs_handle_struct *handle, enum SMB_QUOTA_TYPE qtype,
-			  unid_t id, SMB_DISK_QUOTA *dq)
+static int skel_get_quota(vfs_handle_struct *handle, const char *path,
+			  enum SMB_QUOTA_TYPE qtype, unid_t id,
+			  SMB_DISK_QUOTA *dq)
 {
-	return SMB_VFS_NEXT_GET_QUOTA(handle, qtype, id, dq);
+	return SMB_VFS_NEXT_GET_QUOTA(handle, path, qtype, id, dq);
 }
 
 static int skel_set_quota(vfs_handle_struct *handle, enum SMB_QUOTA_TYPE qtype,
diff --git a/source3/include/vfs.h b/source3/include/vfs.h
index 66e4fc6..c18ea59 100644
--- a/source3/include/vfs.h
+++ b/source3/include/vfs.h
@@ -526,7 +526,9 @@ struct vfs_fn_pointers {
 	void (*disconnect_fn)(struct vfs_handle_struct *handle);
 	uint64_t (*disk_free_fn)(struct vfs_handle_struct *handle, const char *path, uint64_t *bsize,
 			      uint64_t *dfree, uint64_t *dsize);
-	int (*get_quota_fn)(struct vfs_handle_struct *handle, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *qt);
+	int (*get_quota_fn)(struct vfs_handle_struct *handle, const char *path,
+			    enum SMB_QUOTA_TYPE qtype, unid_t id,
+			    SMB_DISK_QUOTA *qt);
 	int (*set_quota_fn)(struct vfs_handle_struct *handle, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *qt);
 	int (*get_shadow_copy_data_fn)(struct vfs_handle_struct *handle, struct files_struct *fsp, struct shadow_copy_data *shadow_copy_data, bool labels);
 	int (*statvfs_fn)(struct vfs_handle_struct *handle, const char *path, struct vfs_statvfs_struct *statbuf);
@@ -931,7 +933,7 @@ void smb_vfs_call_disconnect(struct vfs_handle_struct *handle);
 uint64_t smb_vfs_call_disk_free(struct vfs_handle_struct *handle,
 				const char *path, uint64_t *bsize,
 				uint64_t *dfree, uint64_t *dsize);
-int smb_vfs_call_get_quota(struct vfs_handle_struct *handle,
+int smb_vfs_call_get_quota(struct vfs_handle_struct *handle, const char *path,
 			   enum SMB_QUOTA_TYPE qtype, unid_t id,
 			   SMB_DISK_QUOTA *qt);
 int smb_vfs_call_set_quota(struct vfs_handle_struct *handle,
diff --git a/source3/include/vfs_macros.h b/source3/include/vfs_macros.h
index eaf0c19..16d5bfd 100644
--- a/source3/include/vfs_macros.h
+++ b/source3/include/vfs_macros.h
@@ -44,10 +44,10 @@
 #define SMB_VFS_NEXT_DISK_FREE(handle, path, bsize, dfree ,dsize)\
 	smb_vfs_call_disk_free((handle)->next, (path), (bsize), (dfree), (dsize))
 
-#define SMB_VFS_GET_QUOTA(conn, qtype, id, qt) \
-	smb_vfs_call_get_quota((conn)->vfs_handles, (qtype), (id), (qt))
-#define SMB_VFS_NEXT_GET_QUOTA(handle, qtype, id, qt) \
-	smb_vfs_call_get_quota((handle)->next, (qtype), (id), (qt))
+#define SMB_VFS_GET_QUOTA(conn, path, qtype, id, qt)                           \
+	smb_vfs_call_get_quota((conn)->vfs_handles, (path), (qtype), (id), (qt))
+#define SMB_VFS_NEXT_GET_QUOTA(handle, path, qtype, id, qt)                    \
+	smb_vfs_call_get_quota((handle)->next, (path), (qtype), (id), (qt))
 
 #define SMB_VFS_SET_QUOTA(conn, qtype, id, qt) \
 	smb_vfs_call_set_quota((conn)->vfs_handles, (qtype), (id), (qt))
diff --git a/source3/modules/vfs_ceph.c b/source3/modules/vfs_ceph.c
index 0113faa..2bc387a 100644
--- a/source3/modules/vfs_ceph.c
+++ b/source3/modules/vfs_ceph.c
@@ -185,7 +185,9 @@ static uint64_t cephwrap_disk_free(struct vfs_handle_struct *handle,
 	}
 }
 
-static int cephwrap_get_quota(struct vfs_handle_struct *handle,  enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *qt)
+static int cephwrap_get_quota(struct vfs_handle_struct *handle,
+			      const char *path, enum SMB_QUOTA_TYPE qtype,
+			      unid_t id, SMB_DISK_QUOTA *qt)
 {
 	/* libceph: Ceph does not implement this */
 #if 0
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index 819a1a1..0f8e067 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -64,13 +64,15 @@ static uint64_t vfswrap_disk_free(vfs_handle_struct *handle, const char *path,
 	return result;
 }
 
-static int vfswrap_get_quota(struct vfs_handle_struct *handle, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *qt)
+static int vfswrap_get_quota(struct vfs_handle_struct *handle, const char *path,
+			     enum SMB_QUOTA_TYPE qtype, unid_t id,
+			     SMB_DISK_QUOTA *qt)
 {
 #ifdef HAVE_SYS_QUOTAS
 	int result;
 
 	START_PROFILE(syscall_get_quota);
-	result = sys_get_quota(handle->conn->connectpath, qtype, id, qt);
+	result = sys_get_quota(path, qtype, id, qt);
 	END_PROFILE(syscall_get_quota);
 	return result;
 #else
diff --git a/source3/modules/vfs_default_quota.c b/source3/modules/vfs_default_quota.c
index a71d3c2..c8d718c 100644
--- a/source3/modules/vfs_default_quota.c
+++ b/source3/modules/vfs_default_quota.c
@@ -92,11 +92,13 @@
 #define DEFAULT_QUOTA_GID_NOLIMIT(handle) \
 	lp_parm_bool(SNUM((handle)->conn),DEFAULT_QUOTA_NAME,"gid nolimit",DEFAULT_QUOTA_GID_NOLIMIT_DEFAULT)
 
-static int default_quota_get_quota(vfs_handle_struct *handle, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dq)
+static int default_quota_get_quota(vfs_handle_struct *handle, const char *path,
+				   enum SMB_QUOTA_TYPE qtype, unid_t id,
+				   SMB_DISK_QUOTA *dq)
 {
 	int ret = -1;
 
-	if ((ret=SMB_VFS_NEXT_GET_QUOTA(handle, qtype, id, dq))!=0) {
+	if ((ret = SMB_VFS_NEXT_GET_QUOTA(handle, path, qtype, id, dq)) != 0) {
 		return ret;
 	}
 
@@ -122,7 +124,8 @@ static int default_quota_get_quota(vfs_handle_struct *handle, enum SMB_QUOTA_TYP
 				unid_t qid;
 				uint32_t qflags = dq->qflags;
 				qid.uid = DEFAULT_QUOTA_UID(handle);
-				SMB_VFS_NEXT_GET_QUOTA(handle, SMB_USER_QUOTA_TYPE, qid, dq);
+				SMB_VFS_NEXT_GET_QUOTA(
+				    handle, path, SMB_USER_QUOTA_TYPE, qid, dq);
 				dq->qflags = qflags;
 			}
 			break;
@@ -132,7 +135,9 @@ static int default_quota_get_quota(vfs_handle_struct *handle, enum SMB_QUOTA_TYP
 				unid_t qid;
 				uint32_t qflags = dq->qflags;
 				qid.gid = DEFAULT_QUOTA_GID(handle);
-				SMB_VFS_NEXT_GET_QUOTA(handle, SMB_GROUP_QUOTA_TYPE, qid, dq);
+				SMB_VFS_NEXT_GET_QUOTA(handle, path,
+						       SMB_GROUP_QUOTA_TYPE,
+						       qid, dq);
 				dq->qflags = qflags;
 			}
 			break;
diff --git a/source3/modules/vfs_fake_dfq.c b/source3/modules/vfs_fake_dfq.c
index ca8a2fc..4834026 100644
--- a/source3/modules/vfs_fake_dfq.c
+++ b/source3/modules/vfs_fake_dfq.c
@@ -27,9 +27,9 @@
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_VFS
 
-static int dfq_get_quota_do(struct vfs_handle_struct *handle, const char *path,
-			    enum SMB_QUOTA_TYPE qtype, unid_t id,
-			    SMB_DISK_QUOTA *qt);
+static int dfq_get_quota(struct vfs_handle_struct *handle, const char *path,
+			 enum SMB_QUOTA_TYPE qtype, unid_t id,
+			 SMB_DISK_QUOTA *qt);
 
 static uint64_t dfq_load_param(int snum, const char *path, const char *section,
 			       const char *param, uint64_t def_val)
@@ -60,7 +60,7 @@ static bool dfq_disk_quotas(vfs_handle_struct *handle, const char *path,
 	id.uid = geteuid();
 
 	ZERO_STRUCT(D);
-	r = dfq_get_quota_do(handle, path, SMB_USER_QUOTA_TYPE, id, &D);
+	r = dfq_get_quota(handle, path, SMB_USER_QUOTA_TYPE, id, &D);
 
 	/* Use softlimit to determine disk space, except when it has been
 	 * exceeded */
@@ -98,7 +98,7 @@ try_group_quota:
 	id.gid = getegid();
 
 	ZERO_STRUCT(D);
-	r = dfq_get_quota_do(handle, path, SMB_GROUP_QUOTA_TYPE, id, &D);
+	r = dfq_get_quota(handle, path, SMB_GROUP_QUOTA_TYPE, id, &D);
 
 	/* Use softlimit to determine disk space, except when it has been
 	 * exceeded */
@@ -179,9 +179,9 @@ static uint64_t dfq_disk_free(vfs_handle_struct *handle, const char *path,
 	return free_1k;
 }
 
-static int dfq_get_quota_do(struct vfs_handle_struct *handle, const char *path,
-			    enum SMB_QUOTA_TYPE qtype, unid_t id,
-			    SMB_DISK_QUOTA *qt)
+static int dfq_get_quota(struct vfs_handle_struct *handle, const char *path,
+			 enum SMB_QUOTA_TYPE qtype, unid_t id,
+			 SMB_DISK_QUOTA *qt)
 {
 	int rc = 0;
 	int save_errno;
@@ -243,7 +243,7 @@ static int dfq_get_quota_do(struct vfs_handle_struct *handle, const char *path,
 	goto out;
 
 dflt:
-	rc = SMB_VFS_NEXT_GET_QUOTA(handle, qtype, id, qt);
+	rc = SMB_VFS_NEXT_GET_QUOTA(handle, path, qtype, id, qt);
 
 out:
 	save_errno = errno;
@@ -253,14 +253,6 @@ out:
 	return rc;
 }
 
-static int dfq_get_quota(struct vfs_handle_struct *handle,
-			 enum SMB_QUOTA_TYPE qtype, unid_t id,
-			 SMB_DISK_QUOTA *qt)
-{
-	return dfq_get_quota_do(handle, handle->conn->connectpath, qtype, id,
-				qt);
-}
-
 struct vfs_fn_pointers vfs_fake_dfq_fns = {
     /* Disk operations */
 
diff --git a/source3/modules/vfs_full_audit.c b/source3/modules/vfs_full_audit.c
index 11f5d82..904b569 100644
--- a/source3/modules/vfs_full_audit.c
+++ b/source3/modules/vfs_full_audit.c
@@ -666,14 +666,14 @@ static uint64_t smb_full_audit_disk_free(vfs_handle_struct *handle,
 }
 
 static int smb_full_audit_get_quota(struct vfs_handle_struct *handle,
-			   enum SMB_QUOTA_TYPE qtype, unid_t id,
-			   SMB_DISK_QUOTA *qt)
+				    const char *path, enum SMB_QUOTA_TYPE qtype,
+				    unid_t id, SMB_DISK_QUOTA *qt)
 {
 	int result;
 
-	result = SMB_VFS_NEXT_GET_QUOTA(handle, qtype, id, qt);
+	result = SMB_VFS_NEXT_GET_QUOTA(handle, path, qtype, id, qt);
 
-	do_log(SMB_VFS_OP_GET_QUOTA, (result >= 0), handle, "");
+	do_log(SMB_VFS_OP_GET_QUOTA, (result >= 0), handle, "%s", path);
 
 	return result;
 }
diff --git a/source3/modules/vfs_time_audit.c b/source3/modules/vfs_time_audit.c
index 7efad1e..2fc6afd 100644
--- a/source3/modules/vfs_time_audit.c
+++ b/source3/modules/vfs_time_audit.c
@@ -178,15 +178,15 @@ static uint64_t smb_time_audit_disk_free(vfs_handle_struct *handle,
 }
 
 static int smb_time_audit_get_quota(struct vfs_handle_struct *handle,
-				    enum SMB_QUOTA_TYPE qtype, unid_t id,
-				    SMB_DISK_QUOTA *qt)
+				    const char *path, enum SMB_QUOTA_TYPE qtype,
+				    unid_t id, SMB_DISK_QUOTA *qt)
 {
 	int result;
 	struct timespec ts1,ts2;
 	double timediff;
 
 	clock_gettime_mono(&ts1);
-	result = SMB_VFS_NEXT_GET_QUOTA(handle, qtype, id, qt);
+	result = SMB_VFS_NEXT_GET_QUOTA(handle, path, qtype, id, qt);
 	clock_gettime_mono(&ts2);
 	timediff = nsec_time_diff(&ts2,&ts1)*1.0e-9;
 
diff --git a/source3/smbd/ntquotas.c b/source3/smbd/ntquotas.c
index 15fd35b..aa2ec3b 100644
--- a/source3/smbd/ntquotas.c
+++ b/source3/smbd/ntquotas.c
@@ -96,7 +96,7 @@ int vfs_get_ntquota(files_struct *fsp, enum SMB_QUOTA_TYPE qtype, struct dom_sid
 			 sid_string_dbg(psid)));
 	}
 
-	ret = SMB_VFS_GET_QUOTA(fsp->conn, qtype, id, &D);
+	ret = SMB_VFS_GET_QUOTA(fsp->conn, ".", qtype, id, &D);
 
 	if (psid)
 		qt->sid    = *psid;
diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
index 1a2ee3d..89f0ae1 100644
--- a/source3/smbd/vfs.c
+++ b/source3/smbd/vfs.c
@@ -1404,12 +1404,12 @@ uint64_t smb_vfs_call_disk_free(struct vfs_handle_struct *handle,
 	return handle->fns->disk_free_fn(handle, path, bsize, dfree, dsize);
 }
 
-int smb_vfs_call_get_quota(struct vfs_handle_struct *handle,
+int smb_vfs_call_get_quota(struct vfs_handle_struct *handle, const char *path,
 			   enum SMB_QUOTA_TYPE qtype, unid_t id,
 			   SMB_DISK_QUOTA *qt)
 {
 	VFS_FIND(get_quota);
-	return handle->fns->get_quota_fn(handle, qtype, id, qt);
+	return handle->fns->get_quota_fn(handle, path, qtype, id, qt);
 }
 
 int smb_vfs_call_set_quota(struct vfs_handle_struct *handle,
-- 
2.4.3


>From 03dde578825503b101573ddcaf0fa4c942ee5203 Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Sun, 10 Jan 2016 14:10:10 +0200
Subject: [PATCH v3 07/15] vfs_cap: add get_quota function

This is in preparation for handling the quota part
of disk_free via the VFS - each module with a
disk_free_fn should also have a get_quota_fn.

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 source3/modules/vfs_cap.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/source3/modules/vfs_cap.c b/source3/modules/vfs_cap.c
index ab17376..65b0b25 100644
--- a/source3/modules/vfs_cap.c
+++ b/source3/modules/vfs_cap.c
@@ -41,6 +41,19 @@ static uint64_t cap_disk_free(vfs_handle_struct *handle, const char *path,
 	return SMB_VFS_NEXT_DISK_FREE(handle, cappath, bsize, dfree, dsize);
 }
 
+static int cap_get_quota(vfs_handle_struct *handle, const char *path,
+			 enum SMB_QUOTA_TYPE qtype, unid_t id,
+			 SMB_DISK_QUOTA *dq)
+{
+	char *cappath = capencode(talloc_tos(), path);
+
+	if (!cappath) {
+		errno = ENOMEM;
+		return -1;
+	}
+	return SMB_VFS_NEXT_GET_QUOTA(handle, cappath, qtype, id, dq);
+}
+
 static DIR *cap_opendir(vfs_handle_struct *handle, const char *fname, const char *mask, uint32_t attr)
 {
 	char *capname = capencode(talloc_tos(), fname);
@@ -516,6 +529,7 @@ static int cap_fsetxattr(vfs_handle_struct *handle, struct files_struct *fsp, co
 
 static struct vfs_fn_pointers vfs_cap_fns = {
 	.disk_free_fn = cap_disk_free,
+	.get_quota_fn = cap_get_quota,
 	.opendir_fn = cap_opendir,
 	.readdir_fn = cap_readdir,
 	.mkdir_fn = cap_mkdir,
-- 
2.4.3


>From c28d25b1f7670199f6aa9229350106fb68977ee0 Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Sun, 10 Jan 2016 14:13:38 +0200
Subject: [PATCH v3 08/15] vfs_shadow_copy2: add get_quota function

This is in preparation for handling the quota part
of disk_free via the VFS - each module with a
disk_free_fn should also have a get_quota_fn.

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 source3/modules/vfs_shadow_copy2.c | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c
index 018ec88..1e05a45 100644
--- a/source3/modules/vfs_shadow_copy2.c
+++ b/source3/modules/vfs_shadow_copy2.c
@@ -1865,6 +1865,39 @@ static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle,
 	return ret;
 }
 
+static int shadow_copy2_get_quota(vfs_handle_struct *handle, const char *path,
+				  enum SMB_QUOTA_TYPE qtype, unid_t id,
+				  SMB_DISK_QUOTA *dq)
+{
+	time_t timestamp;
+	char *stripped;
+	int ret;
+	int saved_errno;
+	char *conv;
+
+	if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path, &timestamp,
+					 &stripped)) {
+		return -1;
+	}
+	if (timestamp == 0) {
+		return SMB_VFS_NEXT_GET_QUOTA(handle, path, qtype, id, dq);
+	}
+
+	conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
+	TALLOC_FREE(stripped);
+	if (conv == NULL) {
+		return -1;
+	}
+
+	ret = SMB_VFS_NEXT_GET_QUOTA(handle, conv, qtype, id, dq);
+
+	saved_errno = errno;
+	TALLOC_FREE(conv);
+	errno = saved_errno;
+
+	return ret;
+}
+
 static int shadow_copy2_connect(struct vfs_handle_struct *handle,
 				const char *service, const char *user)
 {
@@ -2106,6 +2139,7 @@ static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
 	.connect_fn = shadow_copy2_connect,
 	.opendir_fn = shadow_copy2_opendir,
 	.disk_free_fn = shadow_copy2_disk_free,
+	.get_quota_fn = shadow_copy2_get_quota,
 	.rename_fn = shadow_copy2_rename,
 	.link_fn = shadow_copy2_link,
 	.symlink_fn = shadow_copy2_symlink,
-- 
2.4.3


>From 5d510dc41d72c2ac315f87a4f7a550cd92aaf11a Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Sun, 10 Jan 2016 14:14:58 +0200
Subject: [PATCH v3 09/15] vfs_snapper: add get_quota function

This is in preparation for handling the quota part
of disk_free via the VFS - each module with a
disk_free_fn should also have a get_quota_fn.

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 source3/modules/vfs_snapper.c | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/source3/modules/vfs_snapper.c b/source3/modules/vfs_snapper.c
index ef8951d..cd7904c 100644
--- a/source3/modules/vfs_snapper.c
+++ b/source3/modules/vfs_snapper.c
@@ -2767,6 +2767,39 @@ static uint64_t snapper_gmt_disk_free(vfs_handle_struct *handle,
 	return ret;
 }
 
+static int snapper_gmt_get_quota(vfs_handle_struct *handle, const char *path,
+				 enum SMB_QUOTA_TYPE qtype, unid_t id,
+				 SMB_DISK_QUOTA *dq)
+{
+	time_t timestamp;
+	char *stripped;
+	int ret;
+	int saved_errno;
+	char *conv;
+
+	if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, path, &timestamp,
+					&stripped)) {
+		return -1;
+	}
+	if (timestamp == 0) {
+		return SMB_VFS_NEXT_GET_QUOTA(handle, path, qtype, id, dq);
+	}
+
+	conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
+	TALLOC_FREE(stripped);
+	if (conv == NULL) {
+		return -1;
+	}
+
+	ret = SMB_VFS_NEXT_GET_QUOTA(handle, conv, qtype, id, dq);
+
+	saved_errno = errno;
+	TALLOC_FREE(conv);
+	errno = saved_errno;
+
+	return ret;
+}
+
 
 static struct vfs_fn_pointers snapper_fns = {
 	.snap_check_path_fn = snapper_snap_check_path,
@@ -2775,6 +2808,7 @@ static struct vfs_fn_pointers snapper_fns = {
 	.get_shadow_copy_data_fn = snapper_get_shadow_copy_data,
 	.opendir_fn = snapper_gmt_opendir,
 	.disk_free_fn = snapper_gmt_disk_free,
+	.get_quota_fn = snapper_gmt_get_quota,
 	.rename_fn = snapper_gmt_rename,
 	.link_fn = snapper_gmt_link,
 	.symlink_fn = snapper_gmt_symlink,
-- 
2.4.3


>From 21825936c915be3836e331ab3605543f1b161f7f Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Sun, 10 Jan 2016 15:15:41 +0200
Subject: [PATCH v3 10/15] smbd: refactor disk_free handling

Move most of the logic that handles determination of
disk size and free space from default VFS handler to
the SMB layer - letting the VFS handle the basic task
of querying the file system for general stats and
quota.

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 source3/modules/vfs_default.c |  8 +++++---
 source3/smbd/dfree.c          | 13 +++++++------
 source3/smbd/proto.h          |  3 ++-
 source3/smbd/quotas.c         | 19 ++++++++++---------
 4 files changed, 24 insertions(+), 19 deletions(-)

diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index 0f8e067..2eb9a17 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -58,10 +58,12 @@ static uint64_t vfswrap_disk_free(vfs_handle_struct *handle, const char *path,
 				  uint64_t *bsize, uint64_t *dfree,
 				  uint64_t *dsize)
 {
-	uint64_t result;
+	if (sys_fsusage(path, dfree, dsize) != 0) {
+		return (uint64_t)-1;
+	}
 
-	result = sys_disk_free(handle->conn, path, bsize, dfree, dsize);
-	return result;
+	*bsize = 512;
+	return *dfree / 2;
 }
 
 static int vfswrap_get_quota(struct vfs_handle_struct *handle, const char *path,
diff --git a/source3/smbd/dfree.c b/source3/smbd/dfree.c
index 32a3a69..6b7993c 100644
--- a/source3/smbd/dfree.c
+++ b/source3/smbd/dfree.c
@@ -116,13 +116,14 @@ uint64_t sys_disk_free(connection_struct *conn, const char *path,
 			   syscmd, strerror(errno) ));
 	}
 
-	if (sys_fsusage(path, dfree, dsize) != 0) {
-		DEBUG (0, ("disk_free: sys_fsusage() failed. Error was : %s\n",
-			strerror(errno) ));
+	if (SMB_VFS_DISK_FREE(conn, path, bsize, dfree, dsize) ==
+	    (uint64_t)-1) {
+		DBG_ERR("VFS disk_free failed. Error was : %s\n",
+			strerror(errno));
 		return (uint64_t)-1;
 	}
 
-	if (disk_quotas(path, &bsize_q, &dfree_q, &dsize_q)) {
+	if (disk_quotas(conn, path, &bsize_q, &dfree_q, &dsize_q)) {
 		(*bsize) = bsize_q;
 		(*dfree) = MIN(*dfree,dfree_q);
 		(*dsize) = MIN(*dsize,dsize_q);
@@ -170,7 +171,7 @@ uint64_t get_dfree_info(connection_struct *conn,
 	uint64_t dfree_ret;
 
 	if (!dfree_cache_time) {
-		return SMB_VFS_DISK_FREE(conn, path, bsize, dfree, dsize);
+		return sys_disk_free(conn, path, bsize, dfree, dsize);
 	}
 
 	if (dfc && (conn->lastused - dfc->last_dfree_time < dfree_cache_time)) {
@@ -181,7 +182,7 @@ uint64_t get_dfree_info(connection_struct *conn,
 		return dfc->dfree_ret;
 	}
 
-	dfree_ret = SMB_VFS_DISK_FREE(conn, path, bsize, dfree, dsize);
+	dfree_ret = sys_disk_free(conn, path, bsize, dfree, dsize);
 
 	if (dfree_ret == (uint64_t)-1) {
 		/* Don't cache bad data. */
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 847a191..3479b3b 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -830,7 +830,8 @@ bool fork_echo_handler(struct smbXsrv_connection *xconn);
 
 /* The following definitions come from smbd/quotas.c  */
 
-bool disk_quotas(const char *path, uint64_t *bsize, uint64_t *dfree, uint64_t *dsize);
+bool disk_quotas(connection_struct *conn, const char *path, uint64_t *bsize,
+		 uint64_t *dfree, uint64_t *dsize);
 bool disk_quotas_vxfs(const char *name, char *path, uint64_t *bsize, uint64_t *dfree, uint64_t *dsize);
 
 /* The following definitions come from smbd/reply.c  */
diff --git a/source3/smbd/quotas.c b/source3/smbd/quotas.c
index 3b23e6b..cbdec6b 100644
--- a/source3/smbd/quotas.c
+++ b/source3/smbd/quotas.c
@@ -236,10 +236,8 @@ try to get the disk space from disk quotas (SunOS & Solaris2 version)
 Quota code by Peter Urbanec (amiga at cse.unsw.edu.au).
 ****************************************************************************/
 
-bool disk_quotas(const char *path,
-		uint64_t *bsize,
-		uint64_t *dfree,
-		uint64_t *dsize)
+bool disk_quotas(connection_struct *conn, const char *path, uint64_t *bsize,
+		 uint64_t *dfree, uint64_t *dsize)
 {
 	uid_t euser_id;
 	int ret;
@@ -428,7 +426,8 @@ bool disk_quotas(const char *path,
 try to get the disk space from disk quotas - default version
 ****************************************************************************/
 
-bool disk_quotas(const char *path, uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
+bool disk_quotas(connection_struct *conn, const char *path, uint64_t *bsize,
+		 uint64_t *dfree, uint64_t *dsize)
 {
   int r;
   struct dqblk D;
@@ -658,7 +657,8 @@ bool disk_quotas_vxfs(const char *name, char *path, uint64_t *bsize, uint64_t *d
 
 #else /* WITH_QUOTAS */
 
-bool disk_quotas(const char *path,uint64_t *bsize,uint64_t *dfree,uint64_t *dsize)
+bool disk_quotas(connection_struct *conn, const char *path, uint64_t *bsize,
+		 uint64_t *dfree, uint64_t *dsize)
 {
 	(*bsize) = 512; /* This value should be ignored */
 
@@ -676,7 +676,8 @@ bool disk_quotas(const char *path,uint64_t *bsize,uint64_t *dfree,uint64_t *dsiz
 /* wrapper to the new sys_quota interface
    this file should be removed later
    */
-bool disk_quotas(const char *path,uint64_t *bsize,uint64_t *dfree,uint64_t *dsize)
+bool disk_quotas(connection_struct *conn, const char *path, uint64_t *bsize,
+		 uint64_t *dfree, uint64_t *dsize)
 {
 	int r;
 	SMB_DISK_QUOTA D;
@@ -685,7 +686,7 @@ bool disk_quotas(const char *path,uint64_t *bsize,uint64_t *dfree,uint64_t *dsiz
 	id.uid = geteuid();
 
 	ZERO_STRUCT(D);
-  	r=sys_get_quota(path, SMB_USER_QUOTA_TYPE, id, &D);
+	r = SMB_VFS_GET_QUOTA(conn, path, SMB_USER_QUOTA_TYPE, id, &D);
 
 	/* Use softlimit to determine disk space, except when it has been exceeded */
 	*bsize = D.bsize;
@@ -723,7 +724,7 @@ try_group_quota:
 	id.gid = getegid();
 
 	ZERO_STRUCT(D);
-  	r=sys_get_quota(path, SMB_GROUP_QUOTA_TYPE, id, &D);
+	r = SMB_VFS_GET_QUOTA(conn, path, SMB_GROUP_QUOTA_TYPE, id, &D);
 
 	/* Use softlimit to determine disk space, except when it has been exceeded */
 	*bsize = D.bsize;
-- 
2.4.3


>From d2927ae75a00ea5adfa8d5cfe8eeec9f10cc97e4 Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Sun, 10 Jan 2016 15:29:32 +0200
Subject: [PATCH v3 11/15] vfs_fake_dfq: remove quota code from disk_free

When mocking disk-free, do not take quota into
account since this is now done in the SMB layer.

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 source3/modules/vfs_fake_dfq.c | 94 ------------------------------------------
 1 file changed, 94 deletions(-)

diff --git a/source3/modules/vfs_fake_dfq.c b/source3/modules/vfs_fake_dfq.c
index 4834026..dea3fe9 100644
--- a/source3/modules/vfs_fake_dfq.c
+++ b/source3/modules/vfs_fake_dfq.c
@@ -50,96 +50,10 @@ static uint64_t dfq_load_param(int snum, const char *path, const char *section,
 	return ret;
 }
 
-static bool dfq_disk_quotas(vfs_handle_struct *handle, const char *path,
-			    uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
-{
-	int r;
-	SMB_DISK_QUOTA D;
-	unid_t id;
-
-	id.uid = geteuid();
-
-	ZERO_STRUCT(D);
-	r = dfq_get_quota(handle, 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 = dfq_get_quota(handle, 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);
-}
-
 static uint64_t dfq_disk_free(vfs_handle_struct *handle, const char *path,
 			      uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
 {
 	uint64_t free_1k;
-	uint64_t dfree_q = 0;
-	uint64_t bsize_q = 0;
-	uint64_t dsize_q = 0;
 	int snum = SNUM(handle->conn);
 	uint64_t dfq_bsize = 0;
 	char *rpath = NULL;
@@ -161,14 +75,6 @@ static uint64_t dfq_disk_free(vfs_handle_struct *handle, const char *path,
 	*dfree = dfq_load_param(snum, rpath, "df", "disk free", 0);
 	*dsize = dfq_load_param(snum, rpath, "df", "disk size", 0);
 
-	if (dfq_disk_quotas(handle, path, &bsize_q, &dfree_q, &dsize_q)) {
-		(*bsize) = bsize_q;
-		(*dfree) = MIN(*dfree, dfree_q);
-		(*dsize) = MIN(*dsize, dsize_q);
-	}
-
-	disk_norm(bsize, dfree, dsize);
-
 	if ((*bsize) < 1024) {
 		free_1k = (*dfree) / (1024 / (*bsize));
 	} else {
-- 
2.4.3


>From cbf943567d47ece584be016ebbae0540d376c544 Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Sun, 10 Jan 2016 15:38:49 +0200
Subject: [PATCH v3 12/15] vfs_ceph: do not call disk_norm() on disk_free_fn

This is handled at SMB layer now.

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 source3/modules/vfs_ceph.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/source3/modules/vfs_ceph.c b/source3/modules/vfs_ceph.c
index 2bc387a..d51499d 100644
--- a/source3/modules/vfs_ceph.c
+++ b/source3/modules/vfs_ceph.c
@@ -175,7 +175,6 @@ static uint64_t cephwrap_disk_free(struct vfs_handle_struct *handle,
 		*bsize = statvfs_buf.f_bsize;
 		*dfree = statvfs_buf.f_bavail;
 		*dsize = statvfs_buf.f_blocks;
-		disk_norm(bsize, dfree, dsize);
 		DEBUG(10, ("[CEPH] bsize: %llu, dfree: %llu, dsize: %llu\n",
 			llu(*bsize), llu(*dfree), llu(*dsize)));
 		return *dfree;
-- 
2.4.3


>From 9a9f3c2a015068ad8f2184b46c94d0b3beec9485 Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Sun, 10 Jan 2016 15:39:44 +0200
Subject: [PATCH v3 13/15] vfs_gpfs: do not call disk_norm() on disk_free_fn

This is handled at the SMB layer now.

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 source3/modules/vfs_gpfs.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/source3/modules/vfs_gpfs.c b/source3/modules/vfs_gpfs.c
index dc53da0..47bbb9d 100644
--- a/source3/modules/vfs_gpfs.c
+++ b/source3/modules/vfs_gpfs.c
@@ -2204,8 +2204,7 @@ static uint64_t vfs_gpfs_disk_free(vfs_handle_struct *handle, const char *path,
 	vfs_gpfs_disk_free_quota(qi_user, cur_time, dfree, dsize);
 	vfs_gpfs_disk_free_quota(qi_group, cur_time, dfree, dsize);
 
-	disk_norm(bsize, dfree, dsize);
-	return *dfree;
+	return *dfree / 2;
 }
 
 static uint32_t vfs_gpfs_capabilities(struct vfs_handle_struct *handle,
-- 
2.4.3


>From 20ae0a2ecdf2ba6b01bf948ddfd5b5d76921cf5f Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Sun, 10 Jan 2016 19:54:06 +0200
Subject: [PATCH v3 14/15] make disk_norm() static

Now that disk_norm() is being run centrally from the SMB layer
it can be made static.

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 source3/smbd/dfree.c | 2 +-
 source3/smbd/proto.h | 1 -
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/source3/smbd/dfree.c b/source3/smbd/dfree.c
index 6b7993c..62d2ea4 100644
--- a/source3/smbd/dfree.c
+++ b/source3/smbd/dfree.c
@@ -25,7 +25,7 @@
  Normalise for DOS usage.
 ****************************************************************************/
 
-void disk_norm(uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
+static void disk_norm(uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
 {
 	/* check if the disk is beyond the max disk size */
 	uint64_t maxdisksize = lp_max_disk_size();
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 3479b3b..9b9c924 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -171,7 +171,6 @@ bool connections_snum_used(struct smbd_server_connection *unused, int snum);
 
 /* The following definitions come from smbd/dfree.c  */
 
-void disk_norm(uint64_t *bsize,uint64_t *dfree,uint64_t *dsize);
 uint64_t sys_disk_free(connection_struct *conn, const char *path,
                               uint64_t *bsize,uint64_t *dfree,uint64_t *dsize);
 uint64_t get_dfree_info(connection_struct *conn,
-- 
2.4.3


>From 71a0573128d5d103c8464b47521bf6a75c4f8bb3 Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Sun, 10 Jan 2016 20:28:57 +0200
Subject: [PATCH v3 15/15] vfs_gpfs: make sure get_quota does not return bogus
 values

add implementation of get_quota_fn to vfs_gpfs. The implemetation
returns ENOSYS for the case of user and group quota, to make sure
the default VFS does not accidentally succeed (and return wrong
values which would alter the disk-free calculation)

For other quota types the function calls the underlying VFS as
before.

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 source3/modules/vfs_gpfs.c | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/source3/modules/vfs_gpfs.c b/source3/modules/vfs_gpfs.c
index 47bbb9d..86be95b 100644
--- a/source3/modules/vfs_gpfs.c
+++ b/source3/modules/vfs_gpfs.c
@@ -2207,6 +2207,30 @@ static uint64_t vfs_gpfs_disk_free(vfs_handle_struct *handle, const char *path,
 	return *dfree / 2;
 }
 
+static int vfs_gpfs_get_quota(vfs_handle_struct *handle, const char *path,
+			  enum SMB_QUOTA_TYPE qtype, unid_t id,
+			  SMB_DISK_QUOTA *dq)
+{
+	switch(qtype) {
+		/* 
+		 * User/group quota are being used for disk-free
+		 * determination, which in this module is done directly
+		 * by the disk-free function. It's important that this
+		 * module does not return wrong quota values by mistake,
+		 * which would modify the correct values set by disk-free.
+		 * User/group quota are also being used for processing
+		 * NT_TRANSACT_GET_USER_QUOTA in smb1 protocol, which is
+		 * currently not supported by this module.
+		 */
+		case SMB_USER_QUOTA_TYPE:
+		case SMB_GROUP_QUOTA_TYPE:
+			errno = ENOSYS;
+			return -1;
+		default:
+			return SMB_VFS_NEXT_GET_QUOTA(handle, path, qtype, id, dq);
+	}
+}
+
 static uint32_t vfs_gpfs_capabilities(struct vfs_handle_struct *handle,
 				      enum timestamp_set_resolution *p_ts_res)
 {
@@ -2445,6 +2469,7 @@ static ssize_t vfs_gpfs_pwrite_recv(struct tevent_req *req, int *err)
 static struct vfs_fn_pointers vfs_gpfs_fns = {
 	.connect_fn = vfs_gpfs_connect,
 	.disk_free_fn = vfs_gpfs_disk_free,
+	.get_quota_fn = vfs_gpfs_get_quota,
 	.fs_capabilities_fn = vfs_gpfs_capabilities,
 	.kernel_flock_fn = vfs_gpfs_kernel_flock,
 	.linux_setlease_fn = vfs_gpfs_setlease,
-- 
2.4.3



More information about the samba-technical mailing list