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

Uri Simchoni uri at samba.org
Mon Jan 11 04:13:54 UTC 2016


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 79ebe55122ddc7b53144bec9ddae341ffcbf5f4f Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Mon, 4 Jan 2016 10:52:16 +0200
Subject: [PATCH 01/19] conv_str_u64(): handle overflow

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 lib/util/util_str.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/util/util_str.c b/lib/util/util_str.c
index 673fbc7..1a67fe1 100644
--- a/lib/util/util_str.c
+++ b/lib/util/util_str.c
@@ -109,8 +109,9 @@ _PUBLIC_ bool conv_str_u64(const char * str, uint64_t * val)
 		return false;
 	}
 
+	errno = 0;
 	lval = strtoull(str, &end, 10 /* base */);
-	if (end == NULL || *end != '\0' || end == str) {
+	if (end == NULL || *end != '\0' || errno == ERANGE) {
 		return false;
 	}
 
-- 
2.4.3


From 276ef3e9a033667cfa2fb763f86b9923c13d6390 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 02/19] vfs_fake_dfq: add vfs module

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

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 source3/modules/vfs_fake_dfq.c | 174 +++++++++++++++++++++++++++++++++++++++++
 source3/modules/wscript_build  |   7 ++
 source3/wscript                |   2 +-
 3 files changed, 182 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..65bb7b9
--- /dev/null
+++ b/source3/modules/vfs_fake_dfq.c
@@ -0,0 +1,174 @@
+/*
+ * 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
+
+#define DFQ_FNAME ".dfq"
+
+enum dfq_param { df_block_size, df_disk_free, df_disk_size, NUM_DFQ_PARAMS };
+
+struct dfq_desc
+{
+	enum dfq_param param;
+	const char *name;
+};
+
+struct dfq_field
+{
+	uint64_t val;
+	bool is_set;
+};
+struct dfq_params
+{
+	struct dfq_field items[NUM_DFQ_PARAMS];
+	char *wanted_section;
+	bool in_section;
+};
+
+static struct dfq_desc dfq_descriptors[] = {
+    {
+     .param = df_block_size, .name = "df block size",
+    },
+    {
+     .param = df_disk_free, .name = "disk free",
+    },
+    {
+     .param = df_disk_size, .name = "disk size",
+    },
+    {.name = NULL}};
+
+static bool do_section(const char *section, void *private_data)
+{
+	struct dfq_params *dfq =
+	    talloc_get_type_abort(private_data, struct dfq_params);
+	dfq->in_section = strequal(dfq->wanted_section, section);
+	return true;
+}
+
+static bool do_param(const char *name, const char *value, void *private_data)
+{
+	int i;
+	enum dfq_param param_type;
+	struct dfq_params *dfq =
+	    talloc_get_type_abort(private_data, struct dfq_params);
+	uint64_t ival;
+
+	if (!dfq->in_section) {
+		return true;
+	}
+
+	if (!conv_str_u64(value, &ival)) {
+		return false;
+	}
+
+	for (i = 0; dfq_descriptors[i].name != NULL; ++i) {
+		if (strequal(name, dfq_descriptors[i].name)) {
+			param_type = dfq_descriptors[i].param;
+			dfq->items[param_type].val = ival;
+			dfq->items[param_type].is_set = true;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static bool load_dfq_params(const char *dirpath, const char *section,
+			    struct dfq_params **_dfq)
+{
+	struct dfq_params *dfq = talloc_zero(talloc_tos(), struct dfq_params);
+	char *conf;
+
+	if (dfq == NULL) {
+		return false;
+	}
+
+	dfq->wanted_section = talloc_strdup(dfq, section);
+	if (dfq->wanted_section == NULL) {
+		TALLOC_FREE(dfq);
+		return false;
+	}
+
+	conf = talloc_asprintf(dfq, "%s/%s", dirpath, DFQ_FNAME);
+
+	if (conf == NULL || !pm_process(conf, do_section, do_param, dfq)) {
+		TALLOC_FREE(dfq);
+		return false;
+	}
+
+	TALLOC_FREE(conf);
+
+	*_dfq = dfq;
+	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;
+	struct dfq_params *dfq = NULL;
+
+	free_1k = SMB_VFS_NEXT_DISK_FREE(handle, path, bsize, dfree, dsize);
+
+	if (!load_dfq_params(path, "df", &dfq)) {
+		goto out;
+	}
+
+	if (!dfq->items[df_block_size].is_set ||
+	    !dfq->items[df_disk_free].is_set ||
+	    !dfq->items[df_disk_size].is_set) {
+		goto out;
+	}
+
+	*bsize = dfq->items[df_block_size].val;
+	*dfree = dfq->items[df_disk_free].val;
+	*dsize = dfq->items[df_disk_size].val;
+
+	disk_norm(bsize, dfree, dsize);
+
+	if ((*bsize) < 1024) {
+		free_1k = (*dfree) / (1024 / (*bsize));
+	} else {
+		free_1k = ((*bsize) / 1024) * (*dfree);
+	}
+
+out:
+	TALLOC_FREE(dfq);
+	return free_1k;
+}
+
+struct vfs_fn_pointers vfs_fake_dfq_fns = {
+    /* Disk operations */
+
+    .disk_free_fn = dfq_disk_free,
+};
+
+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 8741d19..e74fd4f 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 a2b9762db61541b44f491c9d72f9a2a87d8e9c0d 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 03/19] selftest: add disk-free tests based on fake_dfq VFS
 module

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

diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index 0906620..aa34866 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -1553,6 +1553,9 @@ sub provision($$$$$$$$)
 	shell_snap:delete command = $fake_snap_pl --delete
 	# a relative path here fails, the snapshot dir is no longer found
 	shadow:snapdir = $shrdir/.snapshots
+[dfq]
+	path = $shrdir/dfree
+	vfs objects = fake_dfq
 	";
 	close(CONF);
 
diff --git a/source3/script/tests/test_dfree_quota.sh b/source3/script/tests/test_dfree_quota.sh
new file mode 100755
index 0000000..e729495
--- /dev/null
+++ b/source3/script/tests/test_dfree_quota.sh
@@ -0,0 +1,77 @@
+#!/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 WORKDIR SMBCLIENT
+EOF
+exit 1;
+fi
+
+SERVER=$1
+DOMAIN=$2
+USERNAME=$3
+PASSWORD=$4
+WORKDIR=$5
+smbclient=$6
+shift 6
+failed=0
+
+incdir=`dirname $0`/../../../testprogs/blackbox
+. $incdir/subunit.sh
+
+conf_lines() {
+cat <<ABC
+conf1:[df]:df block size = 512:disk free = 10:disk size = 20
+conf2:[df]:df block size = 1024:disk free = 10:disk size = 20
+conf3:[df]:df block size = 4096:disk free = 750:disk size = 281474976710656
+ABC
+}
+
+setup_conf() {
+    conf_name="$1"
+    subdir="$2"
+    conf_lines | sed -rn "s/^$conf_name:(.*)/\1/p" | tr ":" "\n" > $WORKDIR/$subdir/.dfq
+}
+
+clear_all_conf() {
+    find $WORKDIR -name ".dfq" -exec rm {} \;
+}
+
+test_smbclient_dfree() {
+	name="$1"
+	dir="$2"
+    conf="$3"
+    expected="$4"
+	shift
+    shift
+    shift
+    shift
+	subunit_start_test "$name"
+    setup_conf "$conf" "$dir"
+	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
+}
+
+
+clear_all_conf
+#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" conf2 "20 1024. 10" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+test_smbclient_dfree "Test dfree subdir NT1 no quota" "subdir1" conf2 "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`
+
+exit $failed
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index 655c593..ee09c03 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/dfree', smbclient3])
     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])
 
-- 
2.4.3


From 51958a13d675c2e42906ebabcaca6790e7bef598 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 04/19] 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 5249cd42b4802d05978c9a7cc36140bfd5b31e8b Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Sat, 9 Jan 2016 21:17:11 +0200
Subject: [PATCH 05/19] vfs_fake_dfq: add get_quota imlementation

Add VFS implementation of get_quota_fn to mock getting
quota information.

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

diff --git a/source3/modules/vfs_fake_dfq.c b/source3/modules/vfs_fake_dfq.c
index 65bb7b9..bf9b715 100644
--- a/source3/modules/vfs_fake_dfq.c
+++ b/source3/modules/vfs_fake_dfq.c
@@ -29,7 +29,21 @@
 
 #define DFQ_FNAME ".dfq"
 
-enum dfq_param { df_block_size, df_disk_free, df_disk_size, NUM_DFQ_PARAMS };
+enum dfq_param {
+	df_block_size,
+	df_disk_free,
+	df_disk_size,
+	q_block_size,
+	q_hard_limit,
+	q_soft_limit,
+	q_cur_blocks,
+	q_ihard_limit,
+	q_isoft_limit,
+	q_cur_inodes,
+	q_err,
+	q_edquot,
+	NUM_DFQ_PARAMS
+};
 
 struct dfq_desc
 {
@@ -47,6 +61,7 @@ struct dfq_params
 	struct dfq_field items[NUM_DFQ_PARAMS];
 	char *wanted_section;
 	bool in_section;
+	bool found_section;
 };
 
 static struct dfq_desc dfq_descriptors[] = {
@@ -59,6 +74,33 @@ static struct dfq_desc dfq_descriptors[] = {
     {
      .param = df_disk_size, .name = "disk size",
     },
+    {
+     .param = q_block_size, .name = "q block size",
+    },
+    {
+     .param = q_hard_limit, .name = "hard limit",
+    },
+    {
+     .param = q_soft_limit, .name = "soft limit",
+    },
+    {
+     .param = q_cur_blocks, .name = "cur blocks",
+    },
+    {
+     .param = q_ihard_limit, .name = "inode hard limit",
+    },
+    {
+     .param = q_isoft_limit, .name = "inode soft limit",
+    },
+    {
+     .param = q_cur_inodes, .name = "cur inodes",
+    },
+    {
+     .param = q_err, .name = "err",
+    },
+    {
+     .param = q_edquot, .name = "edquot",
+    },
     {.name = NULL}};
 
 static bool do_section(const char *section, void *private_data)
@@ -66,6 +108,9 @@ static bool do_section(const char *section, void *private_data)
 	struct dfq_params *dfq =
 	    talloc_get_type_abort(private_data, struct dfq_params);
 	dfq->in_section = strequal(dfq->wanted_section, section);
+	if (dfq->in_section) {
+		dfq->found_section = true;
+	}
 	return true;
 }
 
@@ -161,10 +206,87 @@ out:
 	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;
+	struct dfq_params *dfq = NULL;
+	char *section = NULL;
+
+	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;
+	}
+
+	if (!load_dfq_params(path, section, &dfq)) {
+		goto dflt;
+	}
+
+	if (!dfq->found_section) {
+		goto dflt;
+	}
+
+	if (dfq->items[q_err].val != 0) {
+		errno = ENOTSUP;
+		rc = -1;
+		goto out;
+	}
+
+	ZERO_STRUCTP(qt);
+
+	qt->bsize = dfq->items[q_block_size].val;
+	qt->hardlimit = dfq->items[q_hard_limit].val;
+	qt->softlimit = dfq->items[q_soft_limit].val;
+	qt->curblocks = dfq->items[q_cur_blocks].val;
+	qt->ihardlimit = dfq->items[q_ihard_limit].val;
+	qt->isoftlimit = dfq->items[q_isoft_limit].val;
+	qt->curinodes = dfq->items[q_cur_inodes].val;
+
+	if (dfq->items[q_edquot].val != 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);
+	TALLOC_FREE(dfq);
+	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)
-- 
2.4.3


From 9d1188d6815db499ce5a88ba0423a5d9f32a2423 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 06/19] 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 109e0a0634138b2a021ec0d2dd7b96e8f377bbcf Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Sat, 9 Jan 2016 22:31:21 +0200
Subject: [PATCH 07/19] selftest: add a blackbox test for fetching user quota
 in SMB1

Use vfs_fake_dfq and smbcquotas to test NT_TRANSACT_GET_USER_QUOTA
processing - this is mainly to ensure nothing gets broken by
planned changes at the VFS layer.

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 selftest/selftesthelpers.py              |  1 +
 selftest/target/Samba3.pm                |  1 +
 source3/script/tests/test_dfree_quota.sh | 35 ++++++++++++++++++++++++++++++--
 source3/selftest/tests.py                |  2 +-
 4 files changed, 36 insertions(+), 3 deletions(-)

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 aa34866..5636a3f 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -1556,6 +1556,7 @@ sub provision($$$$$$$$)
 [dfq]
 	path = $shrdir/dfree
 	vfs objects = fake_dfq
+	admin users = $unix_name
 	";
 	close(CONF);
 
diff --git a/source3/script/tests/test_dfree_quota.sh b/source3/script/tests/test_dfree_quota.sh
index e729495..dbd26da 100755
--- a/source3/script/tests/test_dfree_quota.sh
+++ b/source3/script/tests/test_dfree_quota.sh
@@ -5,7 +5,7 @@
 
 if [ $# -lt 6 ]; then
 cat <<EOF
-Usage: test_dfree_quota.sh SERVER DOMAIN USERNAME PASSWORD WORKDIR SMBCLIENT
+Usage: test_dfree_quota.sh SERVER DOMAIN USERNAME PASSWORD WORKDIR SMBCLIENT SMBCQUOTAS
 EOF
 exit 1;
 fi
@@ -16,17 +16,21 @@ USERNAME=$3
 PASSWORD=$4
 WORKDIR=$5
 smbclient=$6
-shift 6
+smbcquotas=$7
+shift 7
 failed=0
 
 incdir=`dirname $0`/../../../testprogs/blackbox
 . $incdir/subunit.sh
 
 conf_lines() {
+    local uid
+    uid=$(id -u $USERNAME)
 cat <<ABC
 conf1:[df]:df block size = 512:disk free = 10:disk size = 20
 conf2:[df]:df block size = 1024:disk free = 10:disk size = 20
 conf3:[df]:df block size = 4096:disk free = 750:disk size = 281474976710656
+confq1:[u$uid]:q block size = 4096:hard limit = 750:soft limit = 1000:cur blocks = 10
 ABC
 }
 
@@ -66,6 +70,31 @@ test_smbclient_dfree() {
 	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
+}
 
 clear_all_conf
 #basic disk-free tests
@@ -73,5 +102,7 @@ test_smbclient_dfree "Test dfree share root SMB3 no quota" "." conf1 "10 1024. 5
 test_smbclient_dfree "Test dfree subdir SMB3 no quota" "subdir1" conf2 "20 1024. 10" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
 test_smbclient_dfree "Test dfree subdir NT1 no quota" "subdir1" conf2 "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`
 
 exit $failed
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index ee09c03..4d57562 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -177,7 +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/dfree', 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/dfree', 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])
 
-- 
2.4.3


From 52a6a925302eb864dfe48fbf159a155755f49393 Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Sat, 9 Jan 2016 23:44:01 +0200
Subject: [PATCH 08/19] vfs_fake_dfq: add quota considerations to disk_free

Add quota considerations to disk_free_fn, based on
the vfs_fake_dfq mocking of quota.

The grans plan is to remove this part 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 | 96 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 96 insertions(+)

diff --git a/source3/modules/vfs_fake_dfq.c b/source3/modules/vfs_fake_dfq.c
index bf9b715..945dd16 100644
--- a/source3/modules/vfs_fake_dfq.c
+++ b/source3/modules/vfs_fake_dfq.c
@@ -64,6 +64,10 @@ struct dfq_params
 	bool found_section;
 };
 
+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 struct dfq_desc dfq_descriptors[] = {
     {
      .param = df_block_size, .name = "df block size",
@@ -171,10 +175,96 @@ static bool load_dfq_params(const char *dirpath, const char *section,
 	return true;
 }
 
+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;
 	struct dfq_params *dfq = NULL;
 
 	free_1k = SMB_VFS_NEXT_DISK_FREE(handle, path, bsize, dfree, dsize);
@@ -193,6 +283,12 @@ static uint64_t dfq_disk_free(vfs_handle_struct *handle, const char *path,
 	*dfree = dfq->items[df_disk_free].val;
 	*dsize = dfq->items[df_disk_size].val;
 
+	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) {
-- 
2.4.3


From a5962f02e23b16c901c2c532ff78b537c09032d1 Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Sun, 10 Jan 2016 09:13:21 +0200
Subject: [PATCH 09/19] selftest: add blackbox tests for get_quota-disk_free
 interaction

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>
---
 source3/script/tests/test_dfree_quota.sh | 45 ++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/source3/script/tests/test_dfree_quota.sh b/source3/script/tests/test_dfree_quota.sh
index dbd26da..da895e1 100755
--- a/source3/script/tests/test_dfree_quota.sh
+++ b/source3/script/tests/test_dfree_quota.sh
@@ -25,12 +25,38 @@ incdir=`dirname $0`/../../../testprogs/blackbox
 
 conf_lines() {
     local uid
+    local gid
     uid=$(id -u $USERNAME)
+    gid=$(id -g $USERNAME)
 cat <<ABC
 conf1:[df]:df block size = 512:disk free = 10:disk size = 20
 conf2:[df]:df block size = 1024:disk free = 10:disk size = 20
 conf3:[df]:df block size = 4096:disk free = 750:disk size = 281474976710656
 confq1:[u$uid]:q block size = 4096:hard limit = 750:soft limit = 1000:cur blocks = 10
+confdfq1:[df]:df block size = 4096:disk free = 10:disk size = 20
+confdfq1:[u$uid]:q block size = 4096:hard limit = 750:soft limit = 1000:cur blocks = 10
+confdfq2:[df]:df block size = 4096:disk free = 10:disk size = 20
+confdfq2:[u$uid]:q block size = 4096:hard limit = 40:soft limit = 40:cur blocks = 37
+confdfq3:[df]:df block size = 4096:disk free = 10:disk size = 80
+confdfq3:[u$uid]:q block size = 4096:hard limit = 40:soft limit = 40:cur blocks = 0
+confdfq4:[df]:df block size = 4096:disk free = 10:disk size = 80
+confdfq4:[u$uid]:q block size = 4096:hard limit = 40:soft limit = 40:cur blocks = 37
+edquot:[df]:df block size = 4096:disk free = 10:disk size = 80
+edquot:[u$uid]:q block size = 4096:hard limit = 40:soft limit = 40:cur blocks = 41:edquot = 1
+slimit:[df]:df block size = 4096:disk free = 10:disk size = 80
+slimit:[u$uid]:q block size = 4096:hard limit = 44:soft limit = 40:cur blocks = 42
+hlimit:[df]:df block size = 4096:disk free = 10:disk size = 80
+hlimit:[u$uid]:q block size = 4096:hard limit = 44:soft limit = 0:cur blocks = 45
+islimit:[df]:df block size = 4096:disk free = 10:disk size = 80
+islimit:[u$uid]:q 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]:df block size = 4096:disk free = 10:disk size = 80
+ihlimit:[u$uid]:q 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]:df block size = 4096:disk free = 10:disk size = 80
+trygrp1:[u$uid]:q block size = 4096:hard limit = 40:soft limit = 40:cur blocks = 41:err = 1
+trygrp1:[g$gid]:q block size = 4096:hard limit = 60:soft limit = 60:cur blocks = 55
+trygrp2:[df]:df block size = 4096:disk free = 10:disk size = 80
+trygrp2:[u$uid]:q block size = 4096:hard limit = 0:soft limit = 0:cur blocks = 41
+trygrp2:[g$gid]:q block size = 4096:hard limit = 60:soft limit = 60:cur blocks = 56
 ABC
 }
 
@@ -105,4 +131,23 @@ test_smbclient_dfree "Test large disk" "." conf3 "1125899906842624 1024. 3000" -
 #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`
+clear_all_conf
+test_smbclient_dfree "Test dfree subdir df vs quota case 4" "subdir1" confdfq4 "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 "164 1024. 0" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+test_smbclient_dfree "Test quota->dfree soft limit" "subdir1" slimit "168 1024. 0" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+test_smbclient_dfree "Test quota->dfree hard limit" "subdir1" hlimit "180 1024. 0" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+test_smbclient_dfree "Test quota->dfree inode soft limit" "subdir1" islimit "148 1024. 0" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+test_smbclient_dfree "Test quota->dfree inode hard limit" "subdir1" ihlimit "148 1024. 0" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
+test_smbclient_dfree "Test quota->dfree err try group" "subdir1" trygrp1 "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 "240 1024. 16" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
 exit $failed
-- 
2.4.3


From 1e06413b27289795c292bab5a388125a693ff012 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 10/19] 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 945dd16..1c79114 100644
--- a/source3/modules/vfs_fake_dfq.c
+++ b/source3/modules/vfs_fake_dfq.c
@@ -64,9 +64,9 @@ struct dfq_params
 	bool found_section;
 };
 
-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 struct dfq_desc dfq_descriptors[] = {
     {
@@ -185,7 +185,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 */
@@ -223,7 +223,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 */
@@ -302,9 +302,9 @@ out:
 	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;
@@ -360,7 +360,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;
@@ -370,14 +370,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 84566fb4e765953b5a6d2eb14817035864f00680 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 11/19] 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 50dda782cc1e32d887c113c91080d2c77774e343 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 12/19] 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 d1673a4..2fc022a 100644
--- a/source3/modules/vfs_shadow_copy2.c
+++ b/source3/modules/vfs_shadow_copy2.c
@@ -1811,6 +1811,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)
 {
@@ -2051,6 +2084,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 a519217eb57d47530cbf0a1e4bc37a00fa15025f 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 13/19] 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 aaf1997572087e82506723b003ad31455c5f85d6 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 14/19] 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 352c34baff57062273e1215dae96dc94638ef000 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 15/19] 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 | 100 -----------------------------------------
 1 file changed, 100 deletions(-)

diff --git a/source3/modules/vfs_fake_dfq.c b/source3/modules/vfs_fake_dfq.c
index 1c79114..0c3b4ee 100644
--- a/source3/modules/vfs_fake_dfq.c
+++ b/source3/modules/vfs_fake_dfq.c
@@ -175,96 +175,10 @@ static bool load_dfq_params(const char *dirpath, const char *section,
 	return true;
 }
 
-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;
 	struct dfq_params *dfq = NULL;
 
 	free_1k = SMB_VFS_NEXT_DISK_FREE(handle, path, bsize, dfree, dsize);
@@ -283,20 +197,6 @@ static uint64_t dfq_disk_free(vfs_handle_struct *handle, const char *path,
 	*dfree = dfq->items[df_disk_free].val;
 	*dsize = dfq->items[df_disk_size].val;
 
-	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);
-	}
-
 out:
 	TALLOC_FREE(dfq);
 	return free_1k;
-- 
2.4.3


From 041f4673b7e96ab0b13a7cd7652ddd74439bc831 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 16/19] 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 5ae6433e60cac6d9f91a2a5cc929cda02d7f95f2 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 17/19] 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 d45a91c19859365652fcef824829125419dc62c1 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 18/19] 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 6dc246be89e7f1556e3a1e1fbfde81977f9fb5c6 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 19/19] 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