[Pkg-samba-maint] [samba] 16/17: Add patches for previous commits

Mathieu Parent sathieu at moszumanska.debian.org
Thu Mar 30 22:15:34 UTC 2017


This is an automated email from the git hooks/post-receive script.

sathieu pushed a commit to branch jessie
in repository samba.

commit e1ea28f62e42e782b4c85fb24430fd6a4fdb95a4
Author: Mathieu Parent <math.parent at gmail.com>
Date:   Thu Mar 30 21:23:34 2017 +0200

    Add patches for previous commits
---
 debian/patches/bug-12721-4.2.patch                |  208 ++++
 debian/patches/fix-shadow_copy2-42-backport.patch |  116 ++
 debian/patches/series                             |    3 +
 debian/patches/shadow_copy2_tests_42.patch        | 1344 +++++++++++++++++++++
 4 files changed, 1671 insertions(+)

diff --git a/debian/patches/bug-12721-4.2.patch b/debian/patches/bug-12721-4.2.patch
new file mode 100644
index 0000000..73b9436
--- /dev/null
+++ b/debian/patches/bug-12721-4.2.patch
@@ -0,0 +1,208 @@
+From 5d4ef6ff0970c93fed49e51a01e63cb67d49d087 Mon Sep 17 00:00:00 2001
+From: Jeremy Allison <jra at samba.org>
+Date: Mon, 27 Mar 2017 10:46:47 -0700
+Subject: [PATCH 1/3] s3: smbd: Fix incorrect logic exposed by fix for the
+ security bug 12496 (CVE-2017-2619).
+
+In a UNIX filesystem, the names "." and ".." by definition can *never*
+be symlinks - they are already reserved names.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=12721
+
+Signed-off-by: Jeremy Allison <jra at samba.org>
+Reviewed-by: Uri Simchoni <uri at samba.org>
+(cherry picked from commit ae17bebd250bdde5614b2ac17e53512f19fe9b68)
+---
+ source3/smbd/vfs.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
+index 4d660122f5a..f084b1d1930 100644
+--- a/source3/smbd/vfs.c
++++ b/source3/smbd/vfs.c
+@@ -1291,8 +1291,11 @@ NTSTATUS check_reduced_name(connection_struct *conn, const char *fname)
+ 			/* fname can't have changed in resolved_path. */
+ 			const char *p = &resolved_name[rootdir_len];
+ 
+-			/* *p can be '\0' if fname was "." */
+-			if (*p == '\0' && ISDOT(fname)) {
++			/*
++			 * UNIX filesystem semantics, names consisting
++			 * only of "." or ".." CANNOT be symlinks.
++			 */
++			if (ISDOT(fname) || ISDOTDOT(fname)) {
+ 				goto out;
+ 			}
+ 
+-- 
+2.12.2.564.g063fe858b8-goog
+
+
+From 71500662d1098d17657b0148a0aa06cd69482c7d Mon Sep 17 00:00:00 2001
+From: Jeremy Allison <jra at samba.org>
+Date: Mon, 27 Mar 2017 17:04:58 -0700
+Subject: [PATCH 2/3] s3: smbd: Fix "follow symlink = no" regression part 2.
+
+Add an extra paramter to cwd_name to check_reduced_name().
+
+If cwd_name == NULL then fname is a client given path relative
+to the root path of the share.
+
+If cwd_name != NULL then fname is a client given path relative
+to cwd_name. cwd_name is relative to the root path of the share.
+
+Not yet used, logic added in the next commit.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=12721
+
+Signed-off-by: Jeremy Allison <jra at samba.org>
+Reviewed-by: Ralph Boehme <slow at samba.org>
+(cherry picked from commit 83e30cb48859b412b76572b6a3ba84d8fde167af)
+---
+ source3/smbd/filename.c |  2 +-
+ source3/smbd/open.c     |  2 +-
+ source3/smbd/proto.h    |  4 +++-
+ source3/smbd/vfs.c      | 10 +++++++++-
+ 4 files changed, 14 insertions(+), 4 deletions(-)
+
+diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c
+index 3593155ce4e..03773c82403 100644
+--- a/source3/smbd/filename.c
++++ b/source3/smbd/filename.c
+@@ -1222,7 +1222,7 @@ NTSTATUS check_name(connection_struct *conn, const char *name)
+ 	}
+ 
+ 	if (!lp_widelinks(SNUM(conn)) || !lp_follow_symlinks(SNUM(conn))) {
+-		status = check_reduced_name(conn,name);
++		status = check_reduced_name(conn, NULL, name);
+ 		if (!NT_STATUS_IS_OK(status)) {
+ 			DEBUG(5,("check_name: name %s failed with %s\n",name,
+ 						nt_errstr(status)));
+diff --git a/source3/smbd/open.c b/source3/smbd/open.c
+index 84177528511..13aaae3b023 100644
+--- a/source3/smbd/open.c
++++ b/source3/smbd/open.c
+@@ -539,7 +539,7 @@ static int non_widelink_open(struct connection_struct *conn,
+ 	}
+ 
+ 	/* Ensure the relative path is below the share. */
+-	status = check_reduced_name(conn, final_component);
++	status = check_reduced_name(conn, parent_dir, final_component);
+ 	if (!NT_STATUS_IS_OK(status)) {
+ 		saved_errno = map_errno_from_nt_status(status);
+ 		goto out;
+diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
+index abfb5435493..032f75de166 100644
+--- a/source3/smbd/proto.h
++++ b/source3/smbd/proto.h
+@@ -1171,7 +1171,9 @@ const char *vfs_readdirname(connection_struct *conn, void *p,
+ 			    SMB_STRUCT_STAT *sbuf, char **talloced);
+ int vfs_ChDir(connection_struct *conn, const char *path);
+ char *vfs_GetWd(TALLOC_CTX *ctx, connection_struct *conn);
+-NTSTATUS check_reduced_name(connection_struct *conn, const char *fname);
++NTSTATUS check_reduced_name(connection_struct *conn,
++			const char *cwd_name,
++			const char *fname);
+ NTSTATUS check_reduced_name_with_privilege(connection_struct *conn,
+ 			const char *fname,
+ 			struct smb_request *smbreq);
+diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
+index f084b1d1930..15896f80b12 100644
+--- a/source3/smbd/vfs.c
++++ b/source3/smbd/vfs.c
+@@ -1163,9 +1163,17 @@ NTSTATUS check_reduced_name_with_privilege(connection_struct *conn,
+ /*******************************************************************
+  Reduce a file name, removing .. elements and checking that
+  it is below dir in the heirachy. This uses realpath.
++
++ If cwd_name == NULL then fname is a client given path relative
++ to the root path of the share.
++
++ If cwd_name != NULL then fname is a client given path relative
++ to cwd_name. cwd_name is relative to the root path of the share.
+ ********************************************************************/
+ 
+-NTSTATUS check_reduced_name(connection_struct *conn, const char *fname)
++NTSTATUS check_reduced_name(connection_struct *conn,
++				const char *cwd_name,
++				const char *fname)
+ {
+ 	char *resolved_name = NULL;
+ 	bool allow_symlinks = true;
+-- 
+2.12.2.564.g063fe858b8-goog
+
+
+From e3fd46264b82ffc22424ee7364b3fd2c0fc14a7e Mon Sep 17 00:00:00 2001
+From: Jeremy Allison <jra at samba.org>
+Date: Mon, 27 Mar 2017 17:09:38 -0700
+Subject: [PATCH 3/3] s3: smbd: Fix "follow symlink = no" regression part 2.
+
+Use the cwd_name parameter to reconstruct the original
+client name for symlink testing.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=12721
+
+Signed-off-by: Jeremy Allison <jra at samba.org>
+Reviewed-by: Ralph Boehme <slow at samba.org>
+(cherry picked from commit e182a4d39e86c9694e255efdf6ee2ea3ccb9af4a)
+---
+ source3/smbd/vfs.c | 23 +++++++++++++++++++++++
+ 1 file changed, 23 insertions(+)
+
+diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
+index 15896f80b12..6c46fe54364 100644
+--- a/source3/smbd/vfs.c
++++ b/source3/smbd/vfs.c
+@@ -1176,6 +1176,7 @@ NTSTATUS check_reduced_name(connection_struct *conn,
+ 				const char *fname)
+ {
+ 	char *resolved_name = NULL;
++	char *new_fname = NULL;
+ 	bool allow_symlinks = true;
+ 	bool allow_widelinks = false;
+ 
+@@ -1317,11 +1318,32 @@ NTSTATUS check_reduced_name(connection_struct *conn,
+ 			}
+ 
+ 			p++;
++
++			/*
++			 * If cwd_name is present and not ".",
++			 * then fname is relative to that, not
++			 * the root of the share. Make sure the
++			 * path we check is the one the client
++			 * sent (cwd_name+fname).
++			 */
++			if (cwd_name != NULL && !ISDOT(cwd_name)) {
++				new_fname = talloc_asprintf(talloc_tos(),
++							"%s/%s",
++							cwd_name,
++							fname);
++				if (new_fname == NULL) {
++					SAFE_FREE(resolved_name);
++					return NT_STATUS_NO_MEMORY;
++				}
++				fname = new_fname;
++			}
++
+ 			if (strcmp(fname, p)!=0) {
+ 				DEBUG(2, ("check_reduced_name: Bad access "
+ 					"attempt: %s is a symlink to %s\n",
+ 					  fname, p));
+ 				SAFE_FREE(resolved_name);
++				TALLOC_FREE(new_fname);
+ 				return NT_STATUS_ACCESS_DENIED;
+ 			}
+ 		}
+@@ -1332,6 +1354,7 @@ NTSTATUS check_reduced_name(connection_struct *conn,
+ 	DEBUG(3,("check_reduced_name: %s reduced to %s\n", fname,
+ 		 resolved_name));
+ 	SAFE_FREE(resolved_name);
++	TALLOC_FREE(new_fname);
+ 	return NT_STATUS_OK;
+ }
+ 
+-- 
+2.12.2.564.g063fe858b8-goog
+
diff --git a/debian/patches/fix-shadow_copy2-42-backport.patch b/debian/patches/fix-shadow_copy2-42-backport.patch
new file mode 100644
index 0000000..e87e6c3
--- /dev/null
+++ b/debian/patches/fix-shadow_copy2-42-backport.patch
@@ -0,0 +1,116 @@
+From fddffa9d84e5d3554f64f0935c7140fda785a477 Mon Sep 17 00:00:00 2001
+From: Uri Simchoni <uri at samba.org>
+Date: Wed, 24 Aug 2016 14:42:23 +0300
+Subject: [PATCH 1/2] vfs_shadow_copy: handle non-existant files and wildcards
+
+During path checking, the vfs connectpath_fn is called to
+determine the share's root, relative to the file being
+queried (for example, in snapshot file this may be other
+than the share's "usual" root directory). connectpath_fn
+must be able to answer this question even if the path does
+not exist and its parent does exist. The convention in this
+case is that this refers to a yet-uncreated file under the parent
+and all queries are relative to the parent.
+
+This also serves as a workaround for the case where connectpath_fn
+has to handle wildcards, as with the case of SMB1 trans2 findfirst.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=12172
+
+Signed-off-by: Uri Simchoni <uri at samba.org>
+Reviewed-by: Jeremy Allison <jra at samba.org>
+
+Autobuild-User(master): Jeremy Allison <jra at samba.org>
+Autobuild-Date(master): Thu Aug 25 05:35:29 CEST 2016 on sn-devel-144
+
+(back-ported from commit f41f439335efb352d03a842c370212a0af77262a)
+---
+ source3/modules/vfs_shadow_copy2.c | 31 ++++++++++++++++++++++++++++++-
+ 1 file changed, 30 insertions(+), 1 deletion(-)
+
+diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c
+index c6fffab..2772a7f 100644
+--- a/source3/modules/vfs_shadow_copy2.c
++++ b/source3/modules/vfs_shadow_copy2.c
+@@ -2151,6 +2151,7 @@ static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
+ 	char *tmp = NULL;
+ 	char *result = NULL;
+ 	int saved_errno = 0;
++	char *parent_dir = NULL;
+ 	size_t rootpath_len = 0;
+ 	struct shadow_copy2_config *config = NULL;
+ 
+@@ -2176,7 +2177,34 @@ static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
+ 	tmp = shadow_copy2_do_convert(talloc_tos(), handle, stripped, timestamp,
+ 				      &rootpath_len);
+ 	if (tmp == NULL) {
+-		goto done;
++		if (errno != ENOENT) {
++			goto done;
++		}
++
++		/*
++		 * If the converted path does not exist, and converting
++		 * the parent yields something that does exist, then
++		 * this path refers to something that has not been
++		 * created yet, relative to the parent path.
++		 * The snapshot finding is relative to the parent.
++		 * (usually snapshots are read/only but this is not
++		 * necessarily true).
++		 * This code also covers getting a wildcard in the
++		 * last component, because this function is called
++		 * prior to sanitizing the path, and in SMB1 we may
++		 * get wildcards in path names.
++		 */
++		if (!parent_dirname(talloc_tos(), stripped, &parent_dir,
++				    NULL)) {
++			errno = ENOMEM;
++			goto done;
++		}
++
++		tmp = shadow_copy2_do_convert(talloc_tos(), handle, parent_dir,
++					      timestamp, &rootpath_len);
++		if (tmp == NULL) {
++			goto done;
++		}
+ 	}
+ 
+ 	DEBUG(10,("converted path is [%s] root path is [%.*s]\n", tmp,
+@@ -2203,6 +2231,7 @@ done:
+ 	}
+ 	TALLOC_FREE(tmp);
+ 	TALLOC_FREE(stripped);
++	TALLOC_FREE(parent_dir);
+ 	if (saved_errno != 0) {
+ 		errno = saved_errno;
+ 	}
+-- 
+2.9.3
+
+
+From 9e8cb52cda24ac82c192a061d7952a63c4a5cc57 Mon Sep 17 00:00:00 2001
+From: Uri Simchoni <uri at samba.org>
+Date: Tue, 28 Mar 2017 08:14:24 +0300
+Subject: [PATCH 2/2] vfs_shadow_copy2: fix crash in 4.2.x backport
+
+Signed-off-by: Uri Simchoni <uri at samba.org>
+---
+ source3/modules/vfs_shadow_copy2.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c
+index 2772a7f..7e38928 100644
+--- a/source3/modules/vfs_shadow_copy2.c
++++ b/source3/modules/vfs_shadow_copy2.c
+@@ -2146,7 +2146,7 @@ static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
+ static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
+ 					    const char *fname)
+ {
+-	time_t timestamp;
++	time_t timestamp = 0;
+ 	char *stripped = NULL;
+ 	char *tmp = NULL;
+ 	char *result = NULL;
+-- 
+2.9.3
+
diff --git a/debian/patches/series b/debian/patches/series
index 40d925d..3c395b6 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -23,3 +23,6 @@ ctdb-Fix-detection-of-gnukfreebsd.patch
 s3-libsmb-Fix-error-where-short-name-length-was-read.patch
 security-2016-12-19.patch
 CVE-2017-2619.patch
+bug-12721-4.2.patch
+fix-shadow_copy2-42-backport.patch
+shadow_copy2_tests_42.patch
diff --git a/debian/patches/shadow_copy2_tests_42.patch b/debian/patches/shadow_copy2_tests_42.patch
new file mode 100644
index 0000000..f73afa0
--- /dev/null
+++ b/debian/patches/shadow_copy2_tests_42.patch
@@ -0,0 +1,1344 @@
+From 4ef595e10a0530eb397e1ef1607c0c5fec5cc176 Mon Sep 17 00:00:00 2001
+From: Uri Simchoni <uri at samba.org>
+Date: Thu, 29 Oct 2015 22:24:30 +0200
+Subject: [PATCH 01/10] vfs_shadow_copy2: add a blackbox test suite
+
+Add a blackbox test suite for vfs_shadow_copy2, testing
+parameters mountpoint, basedir, snapdir, snapdirseverywhere,
+and testing correct wide-link processing.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=11580
+
+Signed-off-by: Uri Simchoni <uri at samba.org>
+Reviewed-by: Michael Adam <obnox at samba.org>
+
+Autobuild-User(master): Michael Adam <obnox at samba.org>
+Autobuild-Date(master): Wed Jan 13 17:11:38 CET 2016 on sn-devel-144
+
+(back-ported from commit 7362c27a62e3802fc8df975ce50115b683811f4a)
+---
+ selftest/target/Samba3.pm                |  80 +++++++++
+ source3/script/tests/test_shadow_copy.sh | 290 +++++++++++++++++++++++++++++++
+ source3/selftest/tests.py                |   2 +
+ 3 files changed, 372 insertions(+)
+ create mode 100755 source3/script/tests/test_shadow_copy.sh
+
+diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
+index bb1fd5b..71e3f4e 100755
+--- a/selftest/target/Samba3.pm
++++ b/selftest/target/Samba3.pm
+@@ -893,6 +893,21 @@ sub provision($$$$$$)
+ 	my $manglenames_shrdir="$shrdir/manglenames";
+ 	push(@dirs,$manglenames_shrdir);
+ 
++	my $widelinks_shrdir="$shrdir/widelinks";
++	push(@dirs,$widelinks_shrdir);
++
++	my $widelinks_linkdir="$shrdir/widelinks_foo";
++	push(@dirs,$widelinks_linkdir);
++
++	my $shadow_tstdir="$shrdir/shadow";
++	push(@dirs,$shadow_tstdir);
++	my $shadow_mntdir="$shadow_tstdir/mount";
++	push(@dirs,$shadow_mntdir);
++	my $shadow_basedir="$shadow_mntdir/base";
++	push(@dirs,$shadow_basedir);
++	my $shadow_shrdir="$shadow_basedir/share";
++	push(@dirs,$shadow_shrdir);
++
+ 	# this gets autocreated by winbindd
+ 	my $wbsockdir="$prefix_abs/winbindd";
+ 	my $wbsockprivdir="$lockdir/winbindd_privileged";
+@@ -1121,6 +1136,10 @@ sub provision($$$$$$)
+ 	# fruit:copyfile is a global option
+ 	fruit:copyfile = yes
+ 
++	#this does not mean that we use non-secure test env,
++	#it just means we ALLOW one to be configured.
++	allow insecure wide links = yes
++
+ 	# Begin extra options
+ 	$extra_options
+ 	# End extra options
+@@ -1260,6 +1279,67 @@ sub provision($$$$$$)
+ [dynamic_share]
+ 	path = $shrdir/%R
+ 	guest ok = yes
++
++[widelinks_share]
++	path = $widelinks_shrdir
++	wide links = no
++	guest ok = yes
++
++[shadow1]
++	path = $shadow_shrdir
++	comment = previous versions snapshots under mount point
++	vfs objects = shadow_copy2
++	shadow:mountpoint = $shadow_mntdir
++
++[shadow2]
++	path = $shadow_shrdir
++	comment = previous versions snapshots outside mount point
++	vfs objects = shadow_copy2
++	shadow:mountpoint = $shadow_mntdir
++	shadow:snapdir = $shadow_tstdir/.snapshots
++
++[shadow3]
++	path = $shadow_shrdir
++	comment = previous versions with subvolume snapshots, snapshots under base dir
++	vfs objects = shadow_copy2
++	shadow:mountpoint = $shadow_mntdir
++	shadow:basedir = $shadow_basedir
++	shadow:snapdir = $shadow_basedir/.snapshots
++
++[shadow4]
++	path = $shadow_shrdir
++	comment = previous versions with subvolume snapshots, snapshots outside mount point
++	vfs objects = shadow_copy2
++	shadow:mountpoint = $shadow_mntdir
++	shadow:basedir = $shadow_basedir
++	shadow:snapdir = $shadow_tstdir/.snapshots
++
++[shadow5]
++	path = $shadow_shrdir
++	comment = previous versions at volume root snapshots under mount point
++	vfs objects = shadow_copy2
++	shadow:mountpoint = $shadow_shrdir
++
++[shadow6]
++	path = $shadow_shrdir
++	comment = previous versions at volume root snapshots outside mount point
++	vfs objects = shadow_copy2
++	shadow:mountpoint = $shadow_shrdir
++	shadow:snapdir = $shadow_tstdir/.snapshots
++
++[shadow7]
++	path = $shadow_shrdir
++	comment = previous versions snapshots everywhere
++	vfs objects = shadow_copy2
++	shadow:mountpoint = $shadow_mntdir
++	shadow:snapdirseverywhere = yes
++
++[shadow_wl]
++	path = $shadow_shrdir
++	comment = previous versions with wide links allowed
++	vfs objects = shadow_copy2
++	shadow:mountpoint = $shadow_mntdir
++	wide links = yes
+ 	";
+ 	close(CONF);
+ 
+diff --git a/source3/script/tests/test_shadow_copy.sh b/source3/script/tests/test_shadow_copy.sh
+new file mode 100755
+index 0000000..eecd5b8
+--- /dev/null
++++ b/source3/script/tests/test_shadow_copy.sh
+@@ -0,0 +1,290 @@
++#!/bin/bash
++#
++# Blackbox test for shadow_copy2 VFS.
++#
++
++if [ $# -lt 7 ]; then
++cat <<EOF
++Usage: test_shadow_copy SERVER SERVER_IP DOMAIN USERNAME PASSWORD WORKDIR SMBCLIENT
++EOF
++exit 1;
++fi
++
++SERVER=${1}
++SERVER_IP=${2}
++DOMAIN=${3}
++USERNAME=${4}
++PASSWORD=${5}
++WORKDIR=${6}
++SMBCLIENT=${7}
++shift 7
++SMBCLIENT="$VALGRIND ${SMBCLIENT}"
++ADDARGS="$*"
++
++incdir=`dirname $0`/../../../testprogs/blackbox
++. $incdir/subunit.sh
++
++SNAPSHOTS[0]='@GMT-2015.10.31-19.40.30'
++SNAPSHOTS[1]='@GMT-2016.10.31-19.40.30'
++SNAPSHOTS[2]='@GMT-2017.10.31-19.40.30'
++SNAPSHOTS[3]='@GMT-2018.10.31-19.40.30'
++SNAPSHOTS[4]='@GMT-2019.10.31-19.40.30'
++SNAPSHOTS[5]='@GMT-2020.10.31-19.40.30'
++SNAPSHOTS[6]='@GMT-2021.10.31-19.40.30'
++SNAPSHOTS[7]='@GMT-2022.10.31-19.40.30'
++SNAPSHOTS[8]='@GMT-2023.10.31-19.40.30'
++SNAPSHOTS[9]='@GMT-2024.10.31-19.40.30'
++
++# build a hierarchy of files, symlinks, and directories
++build_files()
++{
++    local rootdir
++    local prefix
++    local version
++    local destdir
++    rootdir=$1
++    prefix=$2
++    version=$3
++    if [ -n "$prefix" ] ; then
++        destdir=$rootdir/$prefix
++    else
++        destdir=$rootdir
++    fi
++
++    mkdir -p $destdir
++    if [ "$version" = "latest" ] ; then
++        #non-snapshot files
++        # for non-snapshot version, create legit files
++        # so that wide-link checks focus on snapshot files
++        touch $destdir/foo
++        mkdir -p $destdir/bar
++        touch $destdir/bar/baz
++        touch $destdir/bar/lfoo
++        touch $destdir/bar/letcpasswd
++        touch $destdir/bar/loutside
++    elif [ "$version" = "fullsnap" ] ; then
++        #snapshot files
++        touch $destdir/foo
++        mkdir -p $destdir/bar
++        touch $destdir/bar/baz
++        ln -fs ../foo $destdir/bar/lfoo
++        ln -fs /etc/passwd $destdir/bar/letcpasswd
++        ln -fs ../../outside $destdir/bar/loutside
++        touch `dirname $destdir`/outside
++    else #subshare snapshot - at bar
++        touch $destdir/baz
++        ln -fs ../foo $destdir/lfoo
++        ln -fs /etc/passwd $destdir/letcpasswd
++        ln -fs ../../outside $destdir/loutside
++        touch `dirname $destdir`/../outside
++    fi
++
++}
++
++# build a snapshots directory
++build_snapshots()
++{
++    local where     #where to build snapshots
++    local prefix    #prefix from snapshot dir to share root
++    local start     #timestamp index of first snapshot
++    local end       #timestamp index of last snapshot
++    local sub       #creat a snapshot of subtree of share
++    local snapdir
++    local snapname
++    local i
++    local version
++
++    where=$1
++    prefix=$2
++    start=$3
++    end=$4
++    sub=$5
++
++    snapdir=$where/.snapshots
++    mkdir -p $snapdir
++
++    version="fullsnap"
++    if [ "$sub" = "1" ] ; then
++        version="subsnap"
++        prefix=""
++
++        # a valid link target for an inner symlink -
++        # the link is not broken yet should be blocked
++        # by wide link checks
++        touch $snapdir/foo
++    fi
++
++    for i in `seq $start $end` ; do
++        snapname=${SNAPSHOTS[$i]}
++        mkdir $snapdir/$snapname
++        build_files $snapdir/$snapname "$prefix" $version
++    done
++}
++
++# Test listing previous versions of a file
++test_count_versions()
++{
++    local share
++    local path
++    local expected_count
++    local versions
++
++    share=$1
++    path=$2
++    expected_count=$3
++    versions=`$SMBCLIENT -U$USERNAME%$PASSWORD "//$SERVER/$share" -I $SERVER_IP -c "allinfo $path" | grep "^create_time:" | wc -l`
++    if [ "$versions" = "$expected_count" ] ; then
++        true
++    else
++        echo "expected $expected_count versions of $path, got $versions"
++        false
++    fi
++}
++
++# Test fetching a previous version of a file
++test_fetch_snap_file()
++{
++    local share
++    local path
++    local snapidx
++
++    share=$1
++    path=$2
++    snapidx=$3
++    $SMBCLIENT -U$USERNAME%$PASSWORD "//$SERVER/$share" -I $SERVER_IP \
++        -c "get ${SNAPSHOTS[$snapidx]}/$path $WORKDIR/foo"
++}
++
++test_shadow_copy_fixed()
++{
++    local share     #share to contact
++    local where     #where to place snapshots
++    local prefix    #prefix to files inside snapshot
++    local msg
++    local allow_wl
++    local ncopies_allowd
++    local ncopies_blocked
++
++    share=$1
++    where=$2
++    prefix=$3
++    msg=$4
++    allow_wl=$5
++
++    ncopies_allowed=4
++    ncopies_blocked=1
++    if [ -n "$allow_wl" ] ; then
++        ncopies_blocked=4
++    fi
++
++    #delete snapshots from previous tests
++    find $WORKDIR -name ".snapshots" -exec rm -rf {} \; 1>/dev/null 2>&1
++    build_snapshots $WORKDIR/$where "$prefix" 0 2
++
++    testit "$msg - regular file" \
++        test_count_versions $share foo $ncopies_allowed || \
++        failed=`expr $failed + 1`
++
++    testit "$msg - regular file in subdir" \
++        test_count_versions $share bar/baz $ncopies_allowed || \
++        failed=`expr $failed + 1`
++
++    testit "$msg - local symlink" \
++        test_count_versions $share bar/lfoo $ncopies_allowed || \
++        failed=`expr $failed + 1`
++
++    testit "$msg - abs symlink outside" \
++        test_count_versions $share bar/letcpasswd $ncopies_blocked || \
++        failed=`expr $failed + 1`
++
++    testit "$msg - rel symlink outside" \
++        test_count_versions $share bar/loutside $ncopies_blocked || \
++        failed=`expr $failed + 1`
++}
++
++test_shadow_copy_everywhere()
++{
++    local share     #share to contact
++
++    share=$1
++
++    #delete snapshots from previous tests
++    find $WORKDIR -name ".snapshots" -exec rm -rf {} \; 1>/dev/null 2>&1
++    build_snapshots "$WORKDIR/mount" "base/share" 0 0
++    build_snapshots "$WORKDIR/mount/base" "share" 1 2
++    build_snapshots "$WORKDIR/mount/base/share" "" 3 5
++    build_snapshots "$WORKDIR/mount/base/share/bar" "" 6 9 1
++
++    testit "snapshots in each dir - regular file" \
++        test_count_versions $share foo 4 || \
++        failed=`expr $failed + 1`
++
++    testit "snapshots in each dir - regular file in subdir" \
++        test_count_versions $share bar/baz 5 || \
++        failed=`expr $failed + 1`
++
++    testit "snapshots in each dir - local symlink (but outside snapshot)" \
++        test_count_versions $share bar/lfoo 1 || \
++        failed=`expr $failed + 1`
++
++    testit "snapshots in each dir - abs symlink outside" \
++        test_count_versions $share bar/letcpasswd 1 || \
++        failed=`expr $failed + 1`
++
++    testit "snapshots in each dir - rel symlink outside" \
++        test_count_versions $share bar/loutside 1 || \
++        failed=`expr $failed + 1`
++
++    #the previous versions of the file bar/lfoo points to are outside its
++    #snapshot, and are not reachable. However, but previous versions
++    #taken at different, non-overlapping times higher up the
++    #hierarchy are still reachable.
++    testit "fetch a previous version of a regular file" \
++        test_fetch_snap_file $share "bar/baz" 6 || \
++        failed=`expr $failed + 1`
++
++    testit_expect_failure "fetch a (non-existent) previous version of a symlink" \
++        test_fetch_snap_file $share "bar/lfoo" 6 || \
++        failed=`expr $failed + 1`
++
++    testit "fetch a previous version of a symlink via browsing (1)" \
++        test_fetch_snap_file $share "bar/lfoo" 0 || \
++        failed=`expr $failed + 1`
++
++    testit "fetch a previous version of a symlink via browsing (2)" \
++        test_fetch_snap_file $share "bar/lfoo" 1 || \
++        failed=`expr $failed + 1`
++
++    testit "fetch a previous version of a symlink via browsing (3)" \
++        test_fetch_snap_file $share "bar/lfoo" 3 || \
++        failed=`expr $failed + 1`
++
++}
++
++#build "latest" files
++build_files $WORKDIR/mount base/share "latest"
++
++failed=0
++
++# a test with wide links allowed - also to verify that what's later
++# being blocked is a result of server security measures and not
++# a testing artifact.
++test_shadow_copy_fixed shadow_wl mount base/share "shadow copies with wide links allowed" 1
++
++# tests for a fixed snapshot location
++test_shadow_copy_fixed shadow1 mount base/share "full volume snapshots mounted under volume"
++test_shadow_copy_fixed shadow2 . base/share "full volume snapshots mounted outside volume"
++test_shadow_copy_fixed shadow3 mount/base share "sub volume snapshots mounted under snapshot point"
++test_shadow_copy_fixed shadow4 . share "sub volume snapshots mounted outside"
++test_shadow_copy_fixed shadow5 mount/base/share "" "full volume snapshots and share mounted under volume"
++test_shadow_copy_fixed shadow6 . "" "full volume snapshots and share mounted outside"
++
++# tests for snapshot everywhere - one snapshot location
++test_shadow_copy_fixed shadow7 mount base/share "'everywhere' full volume snapshots"
++test_shadow_copy_fixed shadow7 mount/base share "'everywhere' sub volume snapshots"
++test_shadow_copy_fixed shadow7 mount/base/share "" "'everywhere' share snapshots"
++
++# a test for snapshots everywhere - multiple snapshot locations
++test_shadow_copy_everywhere shadow7
++
++exit $failed
+diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
+index d27595c..0b236dc 100755
+--- a/source3/selftest/tests.py
++++ b/source3/selftest/tests.py
+@@ -175,6 +175,8 @@ for env in ["s3dc"]:
+     # encrypted
+     plantestsuite("samba3.blackbox.smbclient_s3.crypt (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_smbclient_s3.sh"), '$SERVER', '$SERVER_IP', '$DOMAIN', '$USERNAME', '$PASSWORD', '$USERID', '$LOCAL_PATH', '$PREFIX', smbclient3, wbinfo, net, configuration, "-e"])
+ 
++for env in ["simpleserver"]:
++    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])
+ 
+     #
+     # tar command tests
+-- 
+2.9.3
+
+
+From 61d637b5bd2953dab5b8d158f97ef9e22457b728 Mon Sep 17 00:00:00 2001
+From: Jeremy Allison <jra at samba.org>
+Date: Fri, 19 Aug 2016 16:58:39 -0700
+Subject: [PATCH 02/10] s3: libsmb: Correctly align create contexts in a create
+ call.
+
+SMB2 shadow copy requests are the first time we've used
+create contexts in anger in this codepath. This took me
+longer than I'd like to admit to find :-).
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=12166
+
+Signed-off-by: Jeremy Allison <jra at samba.org>
+Reviewed-by: Uri Simchoni <uri at samba.org>
+(cherry picked from commit f8caadfc78a15fa3aefc9ef6249195767c47aa8f)
+---
+ libcli/smb/smb2cli_create.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/libcli/smb/smb2cli_create.c b/libcli/smb/smb2cli_create.c
+index 0db546c..778b501 100644
+--- a/libcli/smb/smb2cli_create.c
++++ b/libcli/smb/smb2cli_create.c
+@@ -113,6 +113,7 @@ struct tevent_req *smb2cli_create_send(
+ 	blobs_offset = ((blobs_offset + 3) & ~3);
+ 
+ 	if (blob.length > 0) {
++		blobs_offset = ((blobs_offset + 7) & ~7);
+ 		SIVAL(fixed, 48, blobs_offset + SMB2_HDR_BODY + 56);
+ 		SIVAL(fixed, 52, blob.length);
+ 	}
+-- 
+2.9.3
+
+
+From 6fcfd295946d0d5eddff5bcf6325968173b09477 Mon Sep 17 00:00:00 2001
+From: Jeremy Allison <jra at samba.org>
+Date: Thu, 18 Aug 2016 17:15:01 -0700
+Subject: [PATCH 03/10] s3: libsmb: Add return args to
+ clistr_is_previous_version_path().
+
+Not yet used - we will use these to construct the SMB2 TWrp blob.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=12166
+
+Signed-off-by: Jeremy Allison <jra at samba.org>
+Reviewed-by: Uri Simchoni <uri at samba.org>
+(back-ported from commit 14fd6dca4ef33ee85a2f8578f1ad608d6056da1f)
+---
+ source3/libsmb/clistr.c | 43 +++++++++++++++++++++++++++++++++++++++++++
+ source3/libsmb/proto.h  |  4 ++++
+ 2 files changed, 47 insertions(+)
+
+diff --git a/source3/libsmb/clistr.c b/source3/libsmb/clistr.c
+index f1264f6..154b9a1 100644
+--- a/source3/libsmb/clistr.c
++++ b/source3/libsmb/clistr.c
+@@ -37,3 +37,46 @@ size_t clistr_pull_talloc(TALLOC_CTX *ctx,
+ 				  src_len,
+ 				  flags);
+ }
++
++bool clistr_is_previous_version_path(const char *path,
++		const char **startp,
++		const char **endp,
++		time_t *ptime)
++{
++	char *q;
++	time_t timestamp;
++	struct tm tm;
++	const char *p = strstr_m(path, "@GMT-");
++
++	if (p == NULL) {
++		return false;
++	}
++	if (p > path && (p[-1] != '\\')) {
++		return false;
++	}
++	q = strptime(p, GMT_FORMAT, &tm);
++	if (q == NULL) {
++		return false;
++	}
++	tm.tm_isdst = -1;
++	timestamp = timegm(&tm);
++	if (timestamp == (time_t)-1) {
++		return false;
++	}
++	if (q[0] != '\0' && q[0] != '\\') {
++		return false;
++	}
++	if (startp) {
++		*startp = p;
++	}
++	if (endp) {
++		if (q[0] == '\\') {
++			q++;
++		}
++		*endp = q;
++	}
++	if (ptime) {
++		*ptime = timestamp;
++	}
++	return true;
++}
+diff --git a/source3/libsmb/proto.h b/source3/libsmb/proto.h
+index a4b3c74..db5e874 100644
+--- a/source3/libsmb/proto.h
++++ b/source3/libsmb/proto.h
+@@ -843,6 +843,10 @@ size_t clistr_pull_talloc(TALLOC_CTX *ctx,
+ 			  const void *src,
+ 			  int src_len,
+ 			  int flags);
++bool clistr_is_previous_version_path(const char *path,
++			const char **startp,
++			const char **endp,
++			time_t *ptime);
+ 
+ /* The following definitions come from libsmb/clitrans.c  */
+ 
+-- 
+2.9.3
+
+
+From 6140246c5f8c88f259b436f4f4b5e81c02ce2097 Mon Sep 17 00:00:00 2001
+From: Jeremy Allison <jra at samba.org>
+Date: Tue, 16 Aug 2016 15:26:53 -0700
+Subject: [PATCH 04/10] s3: libsmb: Add cli_smb2_shadow_copy_data() function
+ that gets shadow copy info over SMB2.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=12166
+
+Signed-off-by: Jeremy Allison <jra at samba.org>
+Reviewed-by: Uri Simchoni <uri at samba.org>
+(back ported from commit 0c6329bc152fcf08fcef385d2f7ee829485eb1a6)
+---
+ source3/libsmb/cli_smb2_fnum.c | 230 +++++++++++++++++++++++++++++++++++++++++
+ source3/libsmb/cli_smb2_fnum.h |   6 ++
+ 2 files changed, 236 insertions(+)
+
+diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
+index 95153ec..15eaf96 100644
+--- a/source3/libsmb/cli_smb2_fnum.c
++++ b/source3/libsmb/cli_smb2_fnum.c
+@@ -38,6 +38,8 @@
+ #include "lib/util/tevent_ntstatus.h"
+ #include "../libcli/security/security.h"
+ #include "lib/util_ea.h"
++#include "librpc/gen_ndr/ndr_ioctl.h"
++#include "ntioctl.h"
+ 
+ struct smb2_hnd {
+ 	uint64_t fid_persistent;
+@@ -2578,3 +2580,231 @@ NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
+ 	}
+ 	return NT_STATUS_OK;
+ }
++
++/***************************************************************
++ SMB2 enum shadow copy data.
++***************************************************************/
++
++struct cli_smb2_shadow_copy_data_fnum_state {
++	struct cli_state *cli;
++	uint16_t fnum;
++	struct smb2_hnd *ph;
++	DATA_BLOB out_input_buffer;
++	DATA_BLOB out_output_buffer;
++};
++
++static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq);
++
++static struct tevent_req *cli_smb2_shadow_copy_data_fnum_send(
++					TALLOC_CTX *mem_ctx,
++					struct tevent_context *ev,
++					struct cli_state *cli,
++					uint16_t fnum,
++					bool get_names)
++{
++	struct tevent_req *req, *subreq;
++	struct cli_smb2_close_fnum_state *state;
++	NTSTATUS status;
++
++	req = tevent_req_create(mem_ctx, &state,
++				struct cli_smb2_shadow_copy_data_fnum_state);
++	if (req == NULL) {
++		return NULL;
++	}
++
++	if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
++		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
++		return tevent_req_post(req, ev);
++	}
++
++	state->cli = cli;
++	state->fnum = fnum;
++
++	status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
++	if (tevent_req_nterror(req, status)) {
++		return tevent_req_post(req, ev);
++	}
++
++	/*
++	 * TODO. Under SMB2 we should send a zero max_output_length
++	 * ioctl to get the required size, then send another ioctl
++	 * to get the data, but the current SMB1 implementation just
++	 * does one roundtrip with a 64K buffer size. Do the same
++	 * for now. JRA.
++	 */
++
++	subreq = smb2cli_ioctl_send(state, ev, state->cli->conn,
++			state->cli->timeout,
++			state->cli->smb2.session,
++			state->cli->smb2.tcon,
++			state->ph->fid_persistent, /* in_fid_persistent */
++			state->ph->fid_volatile, /* in_fid_volatile */
++			FSCTL_GET_SHADOW_COPY_DATA,
++			0, /* in_max_input_length */
++			NULL, /* in_input_buffer */
++			get_names ?
++				CLI_BUFFER_SIZE : 16, /* in_max_output_length */
++			NULL, /* in_output_buffer */
++			SMB2_IOCTL_FLAG_IS_FSCTL);
++
++	if (tevent_req_nomem(subreq, req)) {
++		return tevent_req_post(req, ev);
++	}
++	tevent_req_set_callback(subreq,
++				cli_smb2_shadow_copy_data_fnum_done,
++				req);
++
++	return req;
++}
++
++static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq)
++{
++	struct tevent_req *req = tevent_req_callback_data(
++		subreq, struct tevent_req);
++	struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
++		req, struct cli_smb2_shadow_copy_data_fnum_state);
++	NTSTATUS status;
++
++	status = smb2cli_ioctl_recv(subreq, state,
++				&state->out_input_buffer,
++				&state->out_output_buffer);
++	TALLOC_FREE(subreq);
++	if (tevent_req_nterror(req, status)) {
++		return;
++	}
++	tevent_req_done(req);
++}
++
++static NTSTATUS cli_smb2_shadow_copy_data_fnum_recv(struct tevent_req *req,
++				TALLOC_CTX *mem_ctx,
++				bool get_names,
++				char ***pnames,
++				int *pnum_names)
++{
++	struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
++		req, struct cli_smb2_shadow_copy_data_fnum_state);
++	char **names = NULL;
++	uint32_t num_names = 0;
++	uint32_t num_names_returned = 0;
++	uint32_t dlength = 0;
++	uint32_t i;
++	uint8_t *endp = NULL;
++	NTSTATUS status;
++
++	if (tevent_req_is_nterror(req, &status)) {
++		return status;
++	}
++
++	if (state->out_output_buffer.length < 16) {
++		return NT_STATUS_INVALID_NETWORK_RESPONSE;
++	}
++
++	num_names = IVAL(state->out_output_buffer.data, 0);
++	num_names_returned = IVAL(state->out_output_buffer.data, 4);
++	dlength = IVAL(state->out_output_buffer.data, 8);
++
++	if (num_names > 0x7FFFFFFF) {
++		return NT_STATUS_INVALID_NETWORK_RESPONSE;
++	}
++
++	if (get_names == false) {
++		*pnum_names = (int)num_names;
++		return NT_STATUS_OK;
++	}
++	if (num_names != num_names_returned) {
++		return NT_STATUS_INVALID_NETWORK_RESPONSE;
++	}
++	if (dlength + 12 < 12) {
++		return NT_STATUS_INVALID_NETWORK_RESPONSE;
++	}
++	/*
++	 * NB. The below is an allowable return if there are
++	 * more snapshots than the buffer size we told the
++	 * server we can receive. We currently don't support
++	 * this.
++	 */
++	if (dlength + 12 > state->out_output_buffer.length) {
++		return NT_STATUS_INVALID_NETWORK_RESPONSE;
++	}
++	if (state->out_output_buffer.length +
++			(2 * sizeof(SHADOW_COPY_LABEL)) <
++				state->out_output_buffer.length) {
++		return NT_STATUS_INVALID_NETWORK_RESPONSE;
++	}
++
++	names = talloc_array(mem_ctx, char *, num_names_returned);
++	if (names == NULL) {
++		return NT_STATUS_NO_MEMORY;
++	}
++
++	endp = state->out_output_buffer.data +
++			state->out_output_buffer.length;
++
++	for (i=0; i<num_names_returned; i++) {
++		bool ret;
++		uint8_t *src;
++		size_t converted_size;
++
++		src = state->out_output_buffer.data + 12 +
++			(i * 2 * sizeof(SHADOW_COPY_LABEL));
++
++		if (src + (2 * sizeof(SHADOW_COPY_LABEL)) > endp) {
++			return NT_STATUS_INVALID_NETWORK_RESPONSE;
++		}
++		ret = convert_string_talloc(
++			names, CH_UTF16LE, CH_UNIX,
++			src, 2 * sizeof(SHADOW_COPY_LABEL),
++			&names[i], &converted_size);
++		if (!ret) {
++			TALLOC_FREE(names);
++			return NT_STATUS_INVALID_NETWORK_RESPONSE;
++		}
++	}
++	*pnum_names = num_names;
++	*pnames = names;
++	return NT_STATUS_OK;
++}
++
++NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx,
++				struct cli_state *cli,
++				uint16_t fnum,
++				bool get_names,
++				char ***pnames,
++				int *pnum_names)
++{
++	TALLOC_CTX *frame = talloc_stackframe();
++	struct tevent_context *ev;
++	struct tevent_req *req;
++	NTSTATUS status = NT_STATUS_NO_MEMORY;
++
++	if (smbXcli_conn_has_async_calls(cli->conn)) {
++		/*
++		 * Can't use sync call while an async call is in flight
++		 */
++		status = NT_STATUS_INVALID_PARAMETER;
++		goto fail;
++	}
++	ev = samba_tevent_context_init(frame);
++	if (ev == NULL) {
++		goto fail;
++	}
++	req = cli_smb2_shadow_copy_data_fnum_send(frame,
++					ev,
++					cli,
++					fnum,
++					get_names);
++	if (req == NULL) {
++		goto fail;
++	}
++	if (!tevent_req_poll_ntstatus(req, ev, &status)) {
++		goto fail;
++	}
++	status = cli_smb2_shadow_copy_data_fnum_recv(req,
++						mem_ctx,
++						get_names,
++						pnames,
++						pnum_names);
++ fail:
++	TALLOC_FREE(frame);
++	return status;
++}
+diff --git a/source3/libsmb/cli_smb2_fnum.h b/source3/libsmb/cli_smb2_fnum.h
+index 173dba0..cc88bba 100644
+--- a/source3/libsmb/cli_smb2_fnum.h
++++ b/source3/libsmb/cli_smb2_fnum.h
+@@ -176,4 +176,10 @@ struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx,
+ 			size_t size);
+ NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
+ 			size_t *pwritten);
++NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx,
++			struct cli_state *cli,
++			uint16_t fnum,
++			bool get_names,
++			char ***pnames,
++			int *pnum_names);
+ #endif /* __SMB2CLI_FNUM_H__ */
+-- 
+2.9.3
+
+
+From bde0de6a381147a55ffcafda98b31005e8f3b2a3 Mon Sep 17 00:00:00 2001
+From: Jeremy Allison <jra at samba.org>
+Date: Tue, 16 Aug 2016 15:27:55 -0700
+Subject: [PATCH 05/10] s3: libsmb: Plumb new SMB2 shadow copy call into
+ cli_shadow_copy_data().
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=12166
+
+Signed-off-by: Jeremy Allison <jra at samba.org>
+Reviewed-by: Uri Simchoni <uri at samba.org>
+(cherry picked from commit 03bf1f858d1c474f9522cb0f5b264c4f6c2ca5b9)
+---
+ source3/libsmb/clifile.c | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c
+index e4480c6..c5f56f9 100644
+--- a/source3/libsmb/clifile.c
++++ b/source3/libsmb/clifile.c
+@@ -6019,11 +6019,22 @@ NTSTATUS cli_shadow_copy_data(TALLOC_CTX *mem_ctx, struct cli_state *cli,
+ 			      uint16_t fnum, bool get_names,
+ 			      char ***pnames, int *pnum_names)
+ {
+-	TALLOC_CTX *frame = talloc_stackframe();
++	TALLOC_CTX *frame = NULL;
+ 	struct tevent_context *ev;
+ 	struct tevent_req *req;
+ 	NTSTATUS status = NT_STATUS_NO_MEMORY;
+ 
++        if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
++		return cli_smb2_shadow_copy_data(mem_ctx,
++					cli,
++					fnum,
++					get_names,
++					pnames,
++					pnum_names);
++	}
++
++	frame = talloc_stackframe();
++
+ 	if (smbXcli_conn_has_async_calls(cli->conn)) {
+ 		/*
+ 		 * Can't use sync call while an async call is in flight
+-- 
+2.9.3
+
+
+From 4411067b09babe1f7305616a63b7df54c2737611 Mon Sep 17 00:00:00 2001
+From: Jeremy Allison <jra at samba.org>
+Date: Fri, 19 Aug 2016 17:00:25 -0700
+Subject: [PATCH 06/10] s3: libsmb: Add the capability to find a @GMT- path in
+ an SMB2 create and transform to a timewarp token.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=12166
+
+Signed-off-by: Jeremy Allison <jra at samba.org>
+Reviewed-by: Uri Simchoni <uri at samba.org>
+
+Autobuild-User(master): Jeremy Allison <jra at samba.org>
+Autobuild-Date(master): Mon Aug 22 22:59:22 CEST 2016 on sn-devel-144
+
+(back ported from commit 272f5c95cfb3d8035939dada7bd473058c7b6517)
+---
+ source3/libsmb/cli_smb2_fnum.c | 55 +++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 54 insertions(+), 1 deletion(-)
+
+diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
+index 15eaf96..53a6e0f 100644
+--- a/source3/libsmb/cli_smb2_fnum.c
++++ b/source3/libsmb/cli_smb2_fnum.c
+@@ -177,6 +177,11 @@ struct tevent_req *cli_smb2_create_fnum_send(TALLOC_CTX *mem_ctx,
+ {
+ 	struct tevent_req *req, *subreq;
+ 	struct cli_smb2_create_fnum_state *state;
++	size_t fname_len = 0;
++	const char *startp = NULL;
++	const char *endp = NULL;
++	time_t tstamp = (time_t)0;
++	struct smb2_create_blobs *cblobs = NULL;
+ 
+ 	req = tevent_req_create(mem_ctx, &state,
+ 				struct cli_smb2_create_fnum_state);
+@@ -194,10 +199,58 @@ struct tevent_req *cli_smb2_create_fnum_send(TALLOC_CTX *mem_ctx,
+ 		create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
+ 	}
+ 
++	/* Check for @GMT- paths. Remove the @GMT and turn into TWrp if so. */
++	fname_len = strlen(fname);
++	if (clistr_is_previous_version_path(fname, &startp, &endp, &tstamp)) {
++		size_t len_before_gmt = startp - fname;
++		size_t len_after_gmt = fname + fname_len - endp;
++		DATA_BLOB twrp_blob;
++		NTTIME ntt;
++		NTSTATUS status;
++
++		char *new_fname = talloc_array(state, char,
++				len_before_gmt + len_after_gmt + 1);
++
++		if (tevent_req_nomem(new_fname, req)) {
++			return tevent_req_post(req, ev);
++		}
++
++		memcpy(new_fname, fname, len_before_gmt);
++		memcpy(new_fname + len_before_gmt, endp, len_after_gmt + 1);
++		fname = new_fname;
++		fname_len = len_before_gmt + len_after_gmt;
++
++		unix_to_nt_time(&ntt, tstamp);
++		twrp_blob = data_blob_const((const void *)&ntt, 8);
++
++		cblobs = talloc_zero(state, struct smb2_create_blobs);
++		if (tevent_req_nomem(cblobs, req)) {
++			return tevent_req_post(req, ev);
++		}
++
++		status = smb2_create_blob_add(state, cblobs,
++				SMB2_CREATE_TAG_TWRP, twrp_blob);
++		if (!NT_STATUS_IS_OK(status)) {
++			tevent_req_nterror(req, status);
++			return tevent_req_post(req, ev);
++		}
++	}
++
+ 	/* SMB2 is pickier about pathnames. Ensure it doesn't
+ 	   start in a '\' */
+ 	if (*fname == '\\') {
+ 		fname++;
++		fname_len--;
++	}
++
++	/* Or end in a '\' */
++	if (fname_len > 0 && fname[fname_len-1] == '\\') {
++		char *new_fname = talloc_strdup(state, fname);
++		if (tevent_req_nomem(new_fname, req)) {
++			return tevent_req_post(req, ev);
++		}
++		new_fname[fname_len-1] = '\0';
++		fname = new_fname;
+ 	}
+ 
+ 	subreq = smb2cli_create_send(state, ev,
+@@ -213,7 +266,7 @@ struct tevent_req *cli_smb2_create_fnum_send(TALLOC_CTX *mem_ctx,
+ 				     share_access,
+ 				     create_disposition,
+ 				     create_options,
+-				     NULL);
++				     cblobs);
+ 	if (tevent_req_nomem(subreq, req)) {
+ 		return tevent_req_post(req, ev);
+ 	}
+-- 
+2.9.3
+
+
+From 55682a41a6acc23abba0fa90ad70e2b7797e657a Mon Sep 17 00:00:00 2001
+From: Uri Simchoni <uri at samba.org>
+Date: Tue, 16 Aug 2016 07:19:04 +0300
+Subject: [PATCH 07/10] s2-selftest: run shadow_copy2 test both in NT1 and SMB3
+ modes
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=12172
+
+Signed-off-by: Uri Simchoni <uri at samba.org>
+Reviewed-by: Jeremy Allison <jra at samba.org>
+(back ported from commit c695faa7f94feb8c0a02a9e2f0472af20047bf65)
+---
+ source3/script/tests/test_shadow_copy.sh | 4 ++--
+ source3/selftest/tests.py                | 3 ++-
+ 2 files changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/source3/script/tests/test_shadow_copy.sh b/source3/script/tests/test_shadow_copy.sh
+index eecd5b8..6ac76b0 100755
+--- a/source3/script/tests/test_shadow_copy.sh
++++ b/source3/script/tests/test_shadow_copy.sh
+@@ -5,7 +5,7 @@
+ 
+ if [ $# -lt 7 ]; then
+ cat <<EOF
+-Usage: test_shadow_copy SERVER SERVER_IP DOMAIN USERNAME PASSWORD WORKDIR SMBCLIENT
++Usage: test_shadow_copy SERVER SERVER_IP DOMAIN USERNAME PASSWORD WORKDIR SMBCLIENT PARAMS
+ EOF
+ exit 1;
+ fi
+@@ -18,8 +18,8 @@ PASSWORD=${5}
+ WORKDIR=${6}
+ SMBCLIENT=${7}
+ shift 7
+-SMBCLIENT="$VALGRIND ${SMBCLIENT}"
+ ADDARGS="$*"
++SMBCLIENT="$VALGRIND ${SMBCLIENT} ${ADDARGS}"
+ 
+ incdir=`dirname $0`/../../../testprogs/blackbox
+ . $incdir/subunit.sh
+diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
+index 0b236dc..d24b540 100755
+--- a/source3/selftest/tests.py
++++ b/source3/selftest/tests.py
+@@ -176,7 +176,8 @@ for env in ["s3dc"]:
+     plantestsuite("samba3.blackbox.smbclient_s3.crypt (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_smbclient_s3.sh"), '$SERVER', '$SERVER_IP', '$DOMAIN', '$USERNAME', '$PASSWORD', '$USERID', '$LOCAL_PATH', '$PREFIX', smbclient3, wbinfo, net, configuration, "-e"])
+ 
+ for env in ["simpleserver"]:
+-    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])
++    plantestsuite("samba3.blackbox.shadow_copy2 NT1 (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_shadow_copy.sh"), '$SERVER', '$SERVER_IP', '$DOMAIN', '$USERNAME', '$PASSWORD', '$LOCAL_PATH/shadow', smbclient3, '-m', 'NT1'])
++    plantestsuite("samba3.blackbox.shadow_copy2 SMB3 (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_shadow_copy.sh"), '$SERVER', '$SERVER_IP', '$DOMAIN', '$USERNAME', '$PASSWORD', '$LOCAL_PATH/shadow', smbclient3, '-m', 'SMB3'])
+ 
+     #
+     # tar command tests
+-- 
+2.9.3
+
+
+From c7247e7f68e2ac4d54d0bae0322cd5b6e7264dbe Mon Sep 17 00:00:00 2001
+From: Uri Simchoni <uri at samba.org>
+Date: Tue, 23 Aug 2016 11:33:52 +0300
+Subject: [PATCH 08/10] selftest: add content to files created during
+ shadow_copy2 test
+
+This will allow reading them and verifying we got the right version
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=12172
+
+Signed-off-by: Uri Simchoni <uri at samba.org>
+Reviewed-by: Jeremy Allison <jra at samba.org>
+(cherry picked from commit 523046080dd65607eacb901d58ee3b6e54de865e)
+---
+ source3/script/tests/test_shadow_copy.sh | 26 ++++++++++++++------------
+ 1 file changed, 14 insertions(+), 12 deletions(-)
+
+diff --git a/source3/script/tests/test_shadow_copy.sh b/source3/script/tests/test_shadow_copy.sh
+index 6ac76b0..21dcfd6 100755
+--- a/source3/script/tests/test_shadow_copy.sh
++++ b/source3/script/tests/test_shadow_copy.sh
+@@ -42,9 +42,11 @@ build_files()
+     local prefix
+     local version
+     local destdir
++    local content
+     rootdir=$1
+     prefix=$2
+     version=$3
++    content=$4
+     if [ -n "$prefix" ] ; then
+         destdir=$rootdir/$prefix
+     else
+@@ -56,27 +58,27 @@ build_files()
+         #non-snapshot files
+         # for non-snapshot version, create legit files
+         # so that wide-link checks focus on snapshot files
+-        touch $destdir/foo
++        echo "$content" > $destdir/foo
+         mkdir -p $destdir/bar
+-        touch $destdir/bar/baz
+-        touch $destdir/bar/lfoo
+-        touch $destdir/bar/letcpasswd
+-        touch $destdir/bar/loutside
++        echo "$content" > $destdir/bar/baz
++        echo "$content" > $destdir/bar/lfoo
++        echo "$content" > $destdir/bar/letcpasswd
++        echo "$content" > $destdir/bar/loutside
+     elif [ "$version" = "fullsnap" ] ; then
+         #snapshot files
+-        touch $destdir/foo
++        echo "$content" > $destdir/foo
+         mkdir -p $destdir/bar
+-        touch $destdir/bar/baz
++        echo "$content" > $destdir/bar/baz
+         ln -fs ../foo $destdir/bar/lfoo
+         ln -fs /etc/passwd $destdir/bar/letcpasswd
+         ln -fs ../../outside $destdir/bar/loutside
+-        touch `dirname $destdir`/outside
++        echo "$content" > `dirname $destdir`/outside
+     else #subshare snapshot - at bar
+-        touch $destdir/baz
++        echo "$content" > $destdir/baz
+         ln -fs ../foo $destdir/lfoo
+         ln -fs /etc/passwd $destdir/letcpasswd
+         ln -fs ../../outside $destdir/loutside
+-        touch `dirname $destdir`/../outside
++        echo "$content" > `dirname $destdir`/../outside
+     fi
+ 
+ }
+@@ -117,7 +119,7 @@ build_snapshots()
+     for i in `seq $start $end` ; do
+         snapname=${SNAPSHOTS[$i]}
+         mkdir $snapdir/$snapname
+-        build_files $snapdir/$snapname "$prefix" $version
++        build_files $snapdir/$snapname "$prefix" $version "$snapname"
+     done
+ }
+ 
+@@ -262,7 +264,7 @@ test_shadow_copy_everywhere()
+ }
+ 
+ #build "latest" files
+-build_files $WORKDIR/mount base/share "latest"
++build_files $WORKDIR/mount base/share "latest" "latest"
+ 
+ failed=0
+ 
+-- 
+2.9.3
+
+
+From 72764606cf0560ebbb47f536f14ef86459f94ac1 Mon Sep 17 00:00:00 2001
+From: Uri Simchoni <uri at samba.org>
+Date: Tue, 23 Aug 2016 14:03:30 +0300
+Subject: [PATCH 09/10] selftest: check file readability in shadow_copy2 test
+
+Add tests which verify that a snapshot file is readable
+if and only if it its metadata can be retrieved. Also
+verify (in most tests) that file is retrieved from the
+correct snapshot.
+
+Together with the existing test for number of previous
+versions we can stat, this test checks that we can read
+those files, and also that we cannot break out of a snapshot
+if wide links are not allowed.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=12172
+
+Signed-off-by: Uri Simchoni <uri at samba.org>
+Reviewed-by: Jeremy Allison <jra at samba.org>
+(backported from commit 495b8177363bf1930f3afb373ad73caac022f353)
+---
+ source3/script/tests/test_shadow_copy.sh | 42 +++++++++++++++++++++++++++-----
+ 1 file changed, 36 insertions(+), 6 deletions(-)
+
+diff --git a/source3/script/tests/test_shadow_copy.sh b/source3/script/tests/test_shadow_copy.sh
+index 21dcfd6..7ef1060 100755
+--- a/source3/script/tests/test_shadow_copy.sh
++++ b/source3/script/tests/test_shadow_copy.sh
+@@ -129,18 +129,48 @@ test_count_versions()
+     local share
+     local path
+     local expected_count
++    local skip_content
+     local versions
++    local tstamps
++    local tstamp
++    local content
+ 
+     share=$1
+     path=$2
+     expected_count=$3
++    skip_content=$4
+     versions=`$SMBCLIENT -U$USERNAME%$PASSWORD "//$SERVER/$share" -I $SERVER_IP -c "allinfo $path" | grep "^create_time:" | wc -l`
+-    if [ "$versions" = "$expected_count" ] ; then
+-        true
+-    else
++    if [ "$versions" != "$expected_count" ] ; then
+         echo "expected $expected_count versions of $path, got $versions"
+-        false
++        return 1
+     fi
++
++    #readable snapshots
++    tstamps=`$SMBCLIENT -U$USERNAME%$PASSWORD "//$SERVER/$share" -I $SERVER_IP -c "allinfo $path" | awk '/^@GMT-/ {snapshot=$1} /^create_time:/ {printf "%s\n", snapshot}'`
++    for tstamp in $tstamps ; do
++        if ! $SMBCLIENT -U$USERNAME%$PASSWORD "//$SERVER/$share" -I $SERVER_IP -c "get $tstamp\\$path $WORKDIR/foo" ; then
++            echo "Failed getting \\\\$SERVER\\$share\\$tstamp\\$path"
++            return 1
++        fi
++        #also check the content, but not for wide links
++        if [ "x$skip_content" != "x1" ] ; then
++            content=`cat $WORKDIR/foo`
++            if [ "$content" != "$tstamp" ] ; then
++                echo "incorrect content of \\\\$SERVER\\$share\\$tstamp\\$path expected [$tstamp]  got [$content]"
++                return 1
++            fi
++        fi
++    done
++
++    #non-readable snapshots
++    tstamps=`$SMBCLIENT -U$USERNAME%$PASSWORD "//$SERVER/$share" -I $SERVER_IP -c "allinfo $path" | \
++        awk '/^@GMT-/ {if (snapshot!=""){printf "%s\n", snapshot} ; snapshot=$1} /^create_time:/ {snapshot=""} END {if (snapshot!=""){printf "%s\n", snapshot}}'`
++    for tstamp in $tstamps ; do
++        if $SMBCLIENT -U$USERNAME%$PASSWORD "//$SERVER/$share" -I $SERVER_IP -c "get $tstamp\\$path $WORKDIR/foo" ; then
++            echo "Unexpected success getting \\\\$SERVER\\$share\\$tstamp\\$path"
++            return 1
++        fi
++    done
+ }
+ 
+ # Test fetching a previous version of a file
+@@ -196,11 +226,11 @@ test_shadow_copy_fixed()
+         failed=`expr $failed + 1`
+ 
+     testit "$msg - abs symlink outside" \
+-        test_count_versions $share bar/letcpasswd $ncopies_blocked || \
++        test_count_versions $share bar/letcpasswd $ncopies_blocked  1 || \
+         failed=`expr $failed + 1`
+ 
+     testit "$msg - rel symlink outside" \
+-        test_count_versions $share bar/loutside $ncopies_blocked || \
++        test_count_versions $share bar/loutside $ncopies_blocked 1 || \
+         failed=`expr $failed + 1`
+ }
+ 
+-- 
+2.9.3
+
+
+From 747c519ee59f386f905b33e4ce78ddf3521f8194 Mon Sep 17 00:00:00 2001
+From: Uri Simchoni <uri at samba.org>
+Date: Tue, 23 Aug 2016 14:29:39 +0300
+Subject: [PATCH 10/10] selftest: test listing directories inside snapshots
+
+Verify that directories are also listable.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=12172
+
+Signed-off-by: Uri Simchoni <uri at samba.org>
+Reviewed-by: Jeremy Allison <jra at samba.org>
+(back ported from commit 22c3982100a1d6bf67979a0659604942ef6f11f0)
+---
+ source3/script/tests/test_shadow_copy.sh | 40 +++++++++++++++++++++++++++-----
+ 1 file changed, 34 insertions(+), 6 deletions(-)
+
+diff --git a/source3/script/tests/test_shadow_copy.sh b/source3/script/tests/test_shadow_copy.sh
+index 7ef1060..dfc22ec 100755
+--- a/source3/script/tests/test_shadow_copy.sh
++++ b/source3/script/tests/test_shadow_copy.sh
+@@ -134,6 +134,7 @@ test_count_versions()
+     local tstamps
+     local tstamp
+     local content
++    local is_dir
+ 
+     share=$1
+     path=$2
+@@ -145,13 +146,28 @@ test_count_versions()
+         return 1
+     fi
+ 
++    is_dir=0
++    $SMBCLIENT -U$USERNAME%$PASSWORD "//$SERVER/$share" -I $SERVER_IP -c "allinfo $path" | grep "^attributes:.*D" && is_dir=1
++    if [ $is_dir = 1 ] ; then
++        skip_content=1
++    fi
++
+     #readable snapshots
+     tstamps=`$SMBCLIENT -U$USERNAME%$PASSWORD "//$SERVER/$share" -I $SERVER_IP -c "allinfo $path" | awk '/^@GMT-/ {snapshot=$1} /^create_time:/ {printf "%s\n", snapshot}'`
+     for tstamp in $tstamps ; do
+-        if ! $SMBCLIENT -U$USERNAME%$PASSWORD "//$SERVER/$share" -I $SERVER_IP -c "get $tstamp\\$path $WORKDIR/foo" ; then
+-            echo "Failed getting \\\\$SERVER\\$share\\$tstamp\\$path"
+-            return 1
++        if [ $is_dir = 0 ] ;
++        then
++            if ! $SMBCLIENT -U$USERNAME%$PASSWORD "//$SERVER/$share" -I $SERVER_IP -c "get $tstamp\\$path $WORKDIR/foo" ; then
++                echo "Failed getting \\\\$SERVER\\$share\\$tstamp\\$path"
++                return 1
++            fi
++        else
++            if ! $SMBCLIENT -U$USERNAME%$PASSWORD "//$SERVER/$share" -I $SERVER_IP -c "ls $tstamp\\$path\\*" ; then
++                echo "Failed listing \\\\$SERVER\\$share\\$tstamp\\$path"
++                return 1
++            fi
+         fi
++
+         #also check the content, but not for wide links
+         if [ "x$skip_content" != "x1" ] ; then
+             content=`cat $WORKDIR/foo`
+@@ -166,9 +182,17 @@ test_count_versions()
+     tstamps=`$SMBCLIENT -U$USERNAME%$PASSWORD "//$SERVER/$share" -I $SERVER_IP -c "allinfo $path" | \
+         awk '/^@GMT-/ {if (snapshot!=""){printf "%s\n", snapshot} ; snapshot=$1} /^create_time:/ {snapshot=""} END {if (snapshot!=""){printf "%s\n", snapshot}}'`
+     for tstamp in $tstamps ; do
+-        if $SMBCLIENT -U$USERNAME%$PASSWORD "//$SERVER/$share" -I $SERVER_IP -c "get $tstamp\\$path $WORKDIR/foo" ; then
+-            echo "Unexpected success getting \\\\$SERVER\\$share\\$tstamp\\$path"
+-            return 1
++        if [ $is_dir = 0 ] ;
++        then
++            if $SMBCLIENT -U$USERNAME%$PASSWORD "//$SERVER/$share" -I $SERVER_IP -c "get $tstamp\\$path $WORKDIR/foo" ; then
++                echo "Unexpected success getting \\\\$SERVER\\$share\\$tstamp\\$path"
++                return 1
++            fi
++        else
++            if $SMBCLIENT -U$USERNAME%$PASSWORD "//$SERVER/$share" -I $SERVER_IP -c "ls $tstamp\\$path\\*" ; then
++                echo "Unexpected success listing \\\\$SERVER\\$share\\$tstamp\\$path"
++                return 1
++            fi
+         fi
+     done
+ }
+@@ -232,6 +256,10 @@ test_shadow_copy_fixed()
+     testit "$msg - rel symlink outside" \
+         test_count_versions $share bar/loutside $ncopies_blocked 1 || \
+         failed=`expr $failed + 1`
++
++    testit "$msg - list directory" \
++        test_count_versions $share bar $ncopies_allowed || \
++        failed=`expr $failed + 1`
+ }
+ 
+ test_shadow_copy_everywhere()
+-- 
+2.9.3
+

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-samba/samba.git




More information about the Pkg-samba-maint mailing list