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

Uri Simchoni uri at samba.org
Mon Jan 18 21:15:56 UTC 2016


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.

Review appreciated.
Thanks,
Uri.

On 01/11/2016 06:13 AM, Uri Simchoni wrote:
> Hi,
>
> The attached patch set is a code refactoring whose aim is to make vfs 
> modules leaner and easier to customize in the area of determining free 
> disk space. Review appreciated.
>
> When calculating disk size and free space (in get_dfree_info()), one 
> has to take both file system metrics and disk quotas into account 
> (that's the Windows behavior - show the smaller of the free space and 
> remaining quota). The current implementation tucks all of this into a 
> disk_free_fn VFS method, which is also supposed to handle the obscure 
> "max disk size" parameter and varying block sizes.
>
> This patch leverages the VFS get_quota_fn interface (with a change) to 
> obtain quota, gets all the generic processing out of the default vfs 
> implementation into the SMB layer, and (in principle) leaves 
> disk_free_fn with the task of querying file system stats. An 
> implementation may still implement everything in disk_free_fn, it just 
> has to return an error from the quota function (this is the status for 
> gpfs after the patch because I didn't want to mess with it too much 
> without testing capability).
>
> In addition to achieving what I believe is a cleaner design, my 
> motivation for doing this is to customize the quota part a bit using a 
> VFS module, to account for a CTERA linux kernel patch that implements 
> default quotas, without having to re-implement the whole thing, and 
> also to add an smb.conf parameter that would calculate quota based on 
> directory owner if owner is inherited (file created in a certain 
> folder by user Y would have user X as their owner, so the correct 
> disk-free stats must take user X quota into account). The latter 
> change is, I believe, of general applicability and will be posted later.
>
> The general patch-set structure:
> Patches 1-9 - add tests for disk-free and quotas, using a new mock vfs 
> module called vfs_fake_dfq. Some of those patches could probably be 
> squashed - the vfs_fake_dfq is built across 3 patches and the tests 
> are added with respective 3 patches. I think it should be easier to 
> review this way but I can squash them.
>
> Patch 10 - adds a path parameter to vfs get_quota_fn
>
> Patches 11-13 add a get_quota_fn to some vfs modules which modify path 
> names
>
> Patch 14 is the core of the refactoring work
>
> Patches 15-17 remove some code from vfs modules that is now handled in 
> a common way (patch 17 also obviates the need for the patch I sent 
> earlier for GPFS).
>
> Patch 18 makes disk_norm() static now that it is only being called 
> from the smb layer.
>
> Patch 19 makes sure gpfs is not broken by this refactoring - I have 
> not tested the gpfs stuff and don't know if this patch is actually 
> required (see the commit message and code for more details).
>
> Thanks,
> Uri

-------------- next part --------------
From b61226009fa791eb574a277d8463a6f4294f0a18 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 v2 01/15] loadparm: introduce lp_parm_ulonglong() and
 lpcfg_parm_ulonglong()

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

diff --git a/lib/param/loadparm.c b/lib/param/loadparm.c
index 9b36c7e..864189a 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,24 @@ 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..d90b234 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -1228,6 +1228,21 @@ 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 20204350468bd96516d2337841fceb6a52383dd3 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 v2 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 9d1c9a7a0c323e3a57afb96bb563ca42a9b123ad 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 v2 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 b589e4ef7d50f881e0f9fcc915517ced2b1c16d2 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 v2 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                |   2 +-
 3 files changed, 283 insertions(+), 1 deletion(-)
 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..57e28e1
--- /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..7589a08 100644
--- a/source3/wscript
+++ b/source3/wscript
@@ -1577,7 +1577,7 @@ main() {
                                       vfs_preopen vfs_catia
                                       vfs_media_harmony vfs_unityed_media vfs_fruit vfs_shell_snap
                                       vfs_commit vfs_worm vfs_crossrename vfs_linux_xfs_sgid
-                                      vfs_time_audit vfs_offline
+                                      vfs_time_audit vfs_offline vfs_fake_dfq
                                   '''))
     default_shared_modules.extend(TO_LIST('auth_script idmap_tdb2 idmap_script'))
     # these have broken dependencies
-- 
2.4.3


From cf0f807a84f60e7a3b854b1ca209257ca6e3abaf 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 v2 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 13b557591322a26590e3166affbabb788ffd3681 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 v2 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 57e28e1..8b37674 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 9abd3a33f73d9045149e847329373cf8466f3a2a 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 v2 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 1d733f09305c2379c7f0a2d6a2c2fb6391217043 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 v2 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 61a3ec400ab7cb4b4b03bcb8ee849d379b061216 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 v2 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 0ee2d19c2891925fc0d9af376ecb6394ec4d3ef7 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 v2 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 3a3c7245164a5c1057de848bfbad6dcc58dd6460 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 v2 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 8b37674..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 0934ad2e0d861d40250bef09af40271d8745424d 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 v2 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 f30d7c5f74ad88c216dcb7299db3315016596445 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 v2 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 e5cba3436c7ffcc32f032b5874c97ac32d5cbdda 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 v2 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 c57c93197c10d98ce4160ff0d1ac0737e3878807 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 v2 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