[PATCH] Provide and test NT ACL aware backup

Andrew Bartlett abartlet at samba.org
Thu Jun 28 02:45:56 UTC 2018


This patch provides the basis for backing up a sysvol (or indeed any
other) share while being aware of the NT ACL of each file (rather than
just stripping it).

Includes are both the ability to set and get the NT ACL locally (via
the pysmbd API against the VFS), as well as getting the NT ACL overSMB. 

The work is by Joe Guo.

I've reviewed it.

Please review and push!

CI: https://gitlab.com/catalyst-samba/samba/pipelines/24724412

Andrew Bartlett
-- 
Andrew Bartlett
https://samba.org/~abartlet/
Authentication Developer, Samba Team         https://samba.org
Samba Development and Support, Catalyst IT   
https://catalyst.net.nz/services/samba



-------------- next part --------------
From 36e43eb5ca467e9ad581aff7ac18be88b6a59357 Mon Sep 17 00:00:00 2001
From: Joe Guo <joeg at catalyst.net.nz>
Date: Wed, 13 Jun 2018 10:39:57 +1200
Subject: [PATCH 1/9] pysmb: add py_smb_unlink and test

Add unlink api to delete a file with a smb connection.
Test added.

Signed-off-by: Joe Guo <joeg at catalyst.net.nz>
---
 python/samba/tests/smb.py |  8 ++++++++
 source4/libcli/pysmb.c    | 24 ++++++++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/python/samba/tests/smb.py b/python/samba/tests/smb.py
index 4df83233357..141e4388e03 100644
--- a/python/samba/tests/smb.py
+++ b/python/samba/tests/smb.py
@@ -53,6 +53,14 @@ class SMBTests(samba.tests.TestCase):
         self.assertIn('Policies',ls,
             msg='"Policies" directory not found in sysvol')
 
+    def test_unlink(self):
+        """
+        The smb.unlink API should delete file
+        """
+        self.conn.savefile(test_file, binary_contents);
+        self.conn.unlink(test_file)
+        self.assertFalse(self.conn.chkpath(test_file))
+
     def test_save_load_text(self):
 
         self.conn.savefile(test_file, test_contents.encode('utf8'))
diff --git a/source4/libcli/pysmb.c b/source4/libcli/pysmb.c
index 5bb3807ab76..a53e30bcf91 100644
--- a/source4/libcli/pysmb.c
+++ b/source4/libcli/pysmb.c
@@ -258,6 +258,27 @@ static PyObject *py_smb_rmdir(PyObject *self, PyObject *args)
 	Py_RETURN_NONE;
 }
 
+
+/*
+ * Remove a file
+ */
+static PyObject *py_smb_unlink(PyObject *self, PyObject *args)
+{
+	NTSTATUS status;
+	const char *filename;
+	struct smb_private_data *spdata;
+
+	if (!PyArg_ParseTuple(args, "s:unlink", &filename)) {
+		return NULL;
+	}
+
+	spdata = pytalloc_get_ptr(self);
+	status = smbcli_unlink(spdata->tree, filename);
+	PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+	Py_RETURN_NONE;
+}
+
 /*
  * Remove a directory and all its contents
  */
@@ -551,6 +572,9 @@ FILE_ATTRIBUTE_ARCHIVE\n\n \
 	{ "rmdir", py_smb_rmdir, METH_VARARGS,
 		"rmdir(path) -> None\n\n \
 		Delete a directory." },
+	{ "unlink", py_smb_unlink, METH_VARARGS,
+		"unlink(path) -> None\n\n \
+		Delete a file." },
 	{ "deltree", py_smb_deltree, METH_VARARGS,
 		"deltree(path) -> None\n\n \
 		Delete a directory and all its contents." },
-- 
2.11.0


From a2b456c1366de3fc652dbddf7910cc55a793e2f9 Mon Sep 17 00:00:00 2001
From: Joe Guo <joeg at catalyst.net.nz>
Date: Fri, 1 Jun 2018 13:40:42 +1200
Subject: [PATCH 2/9] pysmbd: add py_smbd_mkdir

Add mkdir for smbd API.

Signed-off-by: Joe Guo <joeg at catalyst.net.nz>
---
 source3/smbd/pysmbd.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/source3/smbd/pysmbd.c b/source3/smbd/pysmbd.c
index ee918e1d727..a63194aa166 100644
--- a/source3/smbd/pysmbd.c
+++ b/source3/smbd/pysmbd.c
@@ -697,6 +697,54 @@ static PyObject *py_smbd_get_sys_acl(PyObject *self, PyObject *args, PyObject *k
 	return py_acl;
 }
 
+static PyObject *py_smbd_mkdir(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+	const char * const kwnames[] = { "fname", "service", NULL };
+	char *fname, *service = NULL;
+	TALLOC_CTX *frame = talloc_stackframe();
+	struct connection_struct *conn = NULL;
+	struct smb_filename *smb_fname = NULL;
+
+	if (!PyArg_ParseTupleAndKeywords(args,
+					 kwargs,
+					 "s|z",
+					 discard_const_p(char *,
+							 kwnames),
+					 &fname,
+					 &service)) {
+		TALLOC_FREE(frame);
+		return NULL;
+	}
+
+	conn = get_conn_tos(service);
+	if (!conn) {
+		TALLOC_FREE(frame);
+		return NULL;
+	}
+
+	smb_fname = synthetic_smb_fname(talloc_tos(),
+					fname,
+					NULL,
+					NULL,
+					lp_posix_pathnames() ?
+					SMB_FILENAME_POSIX_PATH : 0);
+
+	if (smb_fname == NULL) {
+		TALLOC_FREE(frame);
+		return NULL;
+	}
+
+
+	if (SMB_VFS_MKDIR(conn, smb_fname, 00755) == -1) {
+		DBG_ERR("mkdir error=%d (%s)\n", errno, strerror(errno));
+		TALLOC_FREE(frame);
+		return NULL;
+	}
+
+	TALLOC_FREE(frame);
+	Py_RETURN_NONE;
+}
+
 static PyMethodDef py_smbd_methods[] = {
 	{ "have_posix_acls",
 		(PyCFunction)py_smbd_have_posix_acls, METH_NOARGS,
@@ -722,6 +770,9 @@ static PyMethodDef py_smbd_methods[] = {
 	{ "unlink",
 		(PyCFunction)py_smbd_unlink, METH_VARARGS|METH_KEYWORDS,
 		NULL },
+	{ "mkdir",
+		(PyCFunction)py_smbd_mkdir, METH_VARARGS|METH_KEYWORDS,
+		NULL },
 	{ NULL }
 };
 
-- 
2.11.0


From 22b615477fc75ee812c8f72a5b822c5844cbf54c Mon Sep 17 00:00:00 2001
From: Joe Guo <joeg at catalyst.net.nz>
Date: Fri, 1 Jun 2018 13:45:25 +1200
Subject: [PATCH 3/9] pysmbd: extract init_files_struct function

Extract initialization code from set_nt_acl_conn for reuse.

Signed-off-by: Joe Guo <joeg at catalyst.net.nz>
---
 source3/smbd/pysmbd.c | 95 ++++++++++++++++++++++++++++++++-------------------
 1 file changed, 59 insertions(+), 36 deletions(-)

diff --git a/source3/smbd/pysmbd.c b/source3/smbd/pysmbd.c
index a63194aa166..cd6ff1f53c5 100644
--- a/source3/smbd/pysmbd.c
+++ b/source3/smbd/pysmbd.c
@@ -37,6 +37,13 @@ extern const struct generic_mapping file_generic_mapping;
 #undef  DBGC_CLASS
 #define DBGC_CLASS DBGC_ACLS
 
+#ifdef O_DIRECTORY
+#define DIRECTORY_FLAGS O_RDONLY|O_DIRECTORY
+#else
+/* POSIX allows us to open a directory with O_RDONLY. */
+#define DIRECTORY_FLAGS O_RDONLY
+#endif
+
 static connection_struct *get_conn_tos(const char *service)
 {
 	struct conn_struct_tos *c = NULL;
@@ -100,25 +107,24 @@ static int set_sys_acl_conn(const char *fname,
 	return ret;
 }
 
-static NTSTATUS set_nt_acl_conn(const char *fname,
-				uint32_t security_info_sent, const struct security_descriptor *sd,
-				connection_struct *conn)
+
+static NTSTATUS init_files_struct(TALLOC_CTX *mem_ctx,
+				  const char *fname,
+				  struct connection_struct *conn,
+				  int flags,
+				  struct files_struct **_fsp)
 {
-	TALLOC_CTX *frame = talloc_stackframe();
-	NTSTATUS status = NT_STATUS_OK;
-	files_struct *fsp;
 	struct smb_filename *smb_fname = NULL;
-	int flags, ret;
+	int ret;
 	mode_t saved_umask;
+	struct files_struct *fsp;
 
-	fsp = talloc_zero(frame, struct files_struct);
+	fsp = talloc_zero(mem_ctx, struct files_struct);
 	if (fsp == NULL) {
-		TALLOC_FREE(frame);
 		return NT_STATUS_NO_MEMORY;
 	}
 	fsp->fh = talloc(fsp, struct fd_handle);
 	if (fsp->fh == NULL) {
-		TALLOC_FREE(frame);
 		return NT_STATUS_NO_MEMORY;
 	}
 	fsp->conn = conn;
@@ -128,42 +134,26 @@ static NTSTATUS set_nt_acl_conn(const char *fname,
 	saved_umask = umask(0);
 
 	smb_fname = synthetic_smb_fname_split(fsp,
-					fname,
-					lp_posix_pathnames());
+					      fname,
+					      lp_posix_pathnames());
 	if (smb_fname == NULL) {
-		TALLOC_FREE(frame);
 		umask(saved_umask);
 		return NT_STATUS_NO_MEMORY;
 	}
 
 	fsp->fsp_name = smb_fname;
-
-#ifdef O_DIRECTORY
-	flags = O_RDONLY|O_DIRECTORY;
-#else
-	/* POSIX allows us to open a directory with O_RDONLY. */
-	flags = O_RDONLY;
-#endif
-
-	fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, O_RDWR, 00400);
-	if (fsp->fh->fd == -1 && errno == EISDIR) {
-		fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, flags, 00400);
-	}
+	fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, flags, 00644);
 	if (fsp->fh->fd == -1) {
-		printf("open: error=%d (%s)\n", errno, strerror(errno));
-		TALLOC_FREE(frame);
 		umask(saved_umask);
-		return NT_STATUS_UNSUCCESSFUL;
+		return NT_STATUS_INVALID_PARAMETER;
 	}
 
 	ret = SMB_VFS_FSTAT(fsp, &smb_fname->st);
 	if (ret == -1) {
 		/* If we have an fd, this stat should succeed. */
-		DEBUG(0,("Error doing fstat on open file %s "
-			"(%s)\n",
-			smb_fname_str_dbg(smb_fname),
-			strerror(errno) ));
-		TALLOC_FREE(frame);
+		DEBUG(0,("Error doing fstat on open file %s (%s)\n",
+			 smb_fname_str_dbg(smb_fname),
+			 strerror(errno) ));
 		umask(saved_umask);
 		return map_nt_error_from_unix(errno);
 	}
@@ -179,7 +169,42 @@ static NTSTATUS set_nt_acl_conn(const char *fname,
 	fsp->sent_oplock_break = NO_BREAK_SENT;
 	fsp->is_directory = S_ISDIR(smb_fname->st.st_ex_mode);
 
-	status = SMB_VFS_FSET_NT_ACL( fsp, security_info_sent, sd);
+	*_fsp = fsp;
+
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS set_nt_acl_conn(const char *fname,
+				uint32_t security_info_sent, const struct security_descriptor *sd,
+				connection_struct *conn)
+{
+	TALLOC_CTX *frame = talloc_stackframe();
+	struct files_struct *fsp = NULL;
+	NTSTATUS status = NT_STATUS_OK;
+
+	/* first, try to open it as a file with flag O_RDWR */
+	status = init_files_struct(frame,
+				   fname,
+				   conn,
+				   O_RDWR,
+				   &fsp);
+	if (!NT_STATUS_IS_OK(status) && errno == EISDIR) {
+		/* if fail, try to open as dir */
+		status = init_files_struct(frame,
+					   fname,
+					   conn,
+					   DIRECTORY_FLAGS,
+					   &fsp);
+	}
+
+	if (!NT_STATUS_IS_OK(status)) {
+		printf("open: error=%d (%s)\n", errno, strerror(errno));
+		SMB_VFS_CLOSE(fsp);
+		TALLOC_FREE(frame);
+		return status;
+	}
+
+	status = SMB_VFS_FSET_NT_ACL(fsp, security_info_sent, sd);
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(0,("set_nt_acl_no_snum: fset_nt_acl returned %s.\n", nt_errstr(status)));
 	}
@@ -187,8 +212,6 @@ static NTSTATUS set_nt_acl_conn(const char *fname,
 	SMB_VFS_CLOSE(fsp);
 
 	TALLOC_FREE(frame);
-
-	umask(saved_umask);
 	return status;
 }
 
-- 
2.11.0


From 183aada2ceff270b22c023b3a4d162b7e3c15012 Mon Sep 17 00:00:00 2001
From: Joe Guo <joeg at catalyst.net.nz>
Date: Fri, 1 Jun 2018 13:48:31 +1200
Subject: [PATCH 4/9] pysmbd: add py_smbd_create_file

Add create_file function to smbd API.

Signed-off-by: Joe Guo <joeg at catalyst.net.nz>
---
 source3/smbd/pysmbd.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/source3/smbd/pysmbd.c b/source3/smbd/pysmbd.c
index cd6ff1f53c5..b220fbe691f 100644
--- a/source3/smbd/pysmbd.c
+++ b/source3/smbd/pysmbd.c
@@ -768,6 +768,51 @@ static PyObject *py_smbd_mkdir(PyObject *self, PyObject *args, PyObject *kwargs)
 	Py_RETURN_NONE;
 }
 
+
+/*
+  Create an empty file
+ */
+static PyObject *py_smbd_create_file(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+	const char * const kwnames[] = { "fname", "service", NULL };
+	char *fname, *service = NULL;
+	TALLOC_CTX *frame = talloc_stackframe();
+	struct connection_struct *conn = NULL;
+	struct files_struct *fsp = NULL;
+	NTSTATUS status;
+
+	if (!PyArg_ParseTupleAndKeywords(args,
+					 kwargs,
+					 "s|z",
+					 discard_const_p(char *,
+							 kwnames),
+					 &fname,
+					 &service)) {
+		TALLOC_FREE(frame);
+		return NULL;
+	}
+
+	conn = get_conn_tos(service);
+	if (!conn) {
+		TALLOC_FREE(frame);
+		return NULL;
+	}
+
+	status = init_files_struct(frame,
+				   fname,
+				   conn,
+				   O_CREAT|O_EXCL|O_RDWR,
+				   &fsp);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_ERR("init_files_struct failed: %s\n",
+			nt_errstr(status));
+	}
+
+	TALLOC_FREE(frame);
+	Py_RETURN_NONE;
+}
+
+
 static PyMethodDef py_smbd_methods[] = {
 	{ "have_posix_acls",
 		(PyCFunction)py_smbd_have_posix_acls, METH_NOARGS,
@@ -796,6 +841,9 @@ static PyMethodDef py_smbd_methods[] = {
 	{ "mkdir",
 		(PyCFunction)py_smbd_mkdir, METH_VARARGS|METH_KEYWORDS,
 		NULL },
+	{ "create_file",
+		(PyCFunction)py_smbd_create_file, METH_VARARGS|METH_KEYWORDS,
+		NULL },
 	{ NULL }
 };
 
-- 
2.11.0


From 4581f8e5fa6f65c887786205187210d631a20287 Mon Sep 17 00:00:00 2001
From: Joe Guo <joeg at catalyst.net.nz>
Date: Fri, 1 Jun 2018 13:50:05 +1200
Subject: [PATCH 5/9] ntacls: add functions to backup and restore ntacls

1. backup a share online from a smb connection with ntacls using pysmb API.
2. backup a share offline from service path with ntacls using pysmbd API.
3. restore from tarfile with pysmdb API.

Signed-off-by: Joe Guo <joeg at catalyst.net.nz>
---
 python/samba/ntacls.py | 327 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 327 insertions(+)

diff --git a/python/samba/ntacls.py b/python/samba/ntacls.py
index 6bb55b416e9..e5178115f66 100644
--- a/python/samba/ntacls.py
+++ b/python/samba/ntacls.py
@@ -21,10 +21,34 @@ from __future__ import print_function
 
 
 import os
+import tarfile
+import tempfile
+import shutil
+
 import samba.xattr_native, samba.xattr_tdb, samba.posix_eadb
+from samba.samba3 import param as s3param
 from samba.dcerpc import security, xattr, idmap
 from samba.ndr import ndr_pack, ndr_unpack
 from samba.samba3 import smbd
+from samba import smb
+
+# don't include volumes
+SMB_FILE_ATTRIBUTE_FLAGS = smb.FILE_ATTRIBUTE_SYSTEM | \
+                           smb.FILE_ATTRIBUTE_DIRECTORY | \
+                           smb.FILE_ATTRIBUTE_ARCHIVE | \
+                           smb.FILE_ATTRIBUTE_HIDDEN
+
+
+SECURITY_SECINFO_FLAGS = security.SECINFO_OWNER | \
+                         security.SECINFO_GROUP | \
+                         security.SECINFO_DACL  | \
+                         security.SECINFO_SACL
+
+
+# SEC_FLAG_SYSTEM_SECURITY is required otherwise get Access Denied
+SECURITY_SEC_FLAGS = security.SEC_FLAG_SYSTEM_SECURITY | \
+                     security.SEC_FLAG_MAXIMUM_ALLOWED
+
 
 class XattrBackendError(Exception):
     """A generic xattr backend error."""
@@ -247,3 +271,306 @@ def dsacl2fsacl(dssddl, sid, as_sddl=True):
         return fdescr
 
     return fdescr.as_sddl(sid)
+
+
+class SMBHelper:
+    """
+    A wrapper class for SMB connection
+
+    smb_path: path with separator "\\" other than "/"
+    """
+
+    def __init__(self, smb_conn, dom_sid):
+        self.smb_conn = smb_conn
+        self.dom_sid = dom_sid
+
+    def get_acl(self, smb_path, as_sddl=False):
+        assert '/' not in smb_path
+
+        ntacl_sd = self.smb_conn.get_acl(
+            smb_path, SECURITY_SECINFO_FLAGS, SECURITY_SEC_FLAGS)
+
+        return ntacl_sd.as_sddl(self.dom_sid) if as_sddl else ntacl_sd
+
+    def list(self, smb_path=''):
+        """
+        List file and dir base names in smb_path without recursive.
+        """
+        assert '/' not in smb_path
+        return self.smb_conn.list(smb_path, attribs=SMB_FILE_ATTRIBUTE_FLAGS)
+
+    def is_dir(self, attrib):
+        """
+        Check whether the attrib value is a directory.
+
+        attrib is from list method.
+        """
+        return bool(attrib & smb.FILE_ATTRIBUTE_DIRECTORY)
+
+    def join(self, root, name):
+        """
+        Join path with '\\'
+        """
+        return root + '\\' + name if root else name
+
+    def loadfile(self, smb_path):
+        assert '/' not in smb_path
+        return self.smb_conn.loadfile(smb_path)
+
+    def create_tree(self, tree, smb_path=''):
+        """
+        Create files as defined in tree
+        """
+        for name, content in tree.items():
+            fullname = self.join(smb_path, name)
+            if isinstance(content, dict):  # a dir
+                if not self.smb_conn.chkpath(fullname):
+                    self.smb_conn.mkdir(fullname)
+                self.create_tree(content, smb_path=fullname)
+            else:  # a file
+                self.smb_conn.savefile(fullname, content)
+
+    def get_tree(self, smb_path=''):
+        """
+        Get the tree structure via smb conn
+
+        self.smb_conn.list example:
+
+        [
+          {
+            'attrib': 16,
+            'mtime': 1528848309,
+            'name': 'dir1',
+            'short_name': 'dir1',
+            'size': 0L
+          }, {
+            'attrib': 32,
+            'mtime': 1528848309,
+            'name': 'file0.txt',
+            'short_name': 'file0.txt',
+            'size': 10L
+          }
+        ]
+        """
+        tree = {}
+        for item in self.list(smb_path):
+            name = item['name']
+            fullname = self.join(smb_path, name)
+            if self.is_dir(item['attrib']):
+                tree[name] = self.get_tree(smb_path=fullname)
+            else:
+                tree[name] = self.loadfile(fullname)
+        return tree
+
+    def get_ntacls(self, smb_path=''):
+        """
+        Get ntacl for each file and dir via smb conn
+        """
+        ntacls = {}
+        for item in self.list(smb_path):
+            name = item['name']
+            fullname = self.join(smb_path, name)
+            if self.is_dir(item['attrib']):
+                ntacls.update(self.get_ntacls(smb_path=fullname))
+            else:
+                ntacl_sd = self.get_acl(fullname)
+                ntacls[fullname] = ntacl_sd.as_sddl(self.dom_sid)
+        return ntacls
+
+    def delete_tree(self):
+        for item in self.list():
+            name = item['name']
+            if self.is_dir(item['attrib']):
+                self.smb_conn.deltree(name)
+            else:
+                self.smb_conn.unlink(name)
+
+
+class NtaclsHelper:
+
+    def __init__(self, service, smb_conf_path, dom_sid):
+        self.service = service
+        self.dom_sid = dom_sid
+
+        # this is important to help smbd find services.
+        self.lp = s3param.get_context()
+        self.lp.load(smb_conf_path)
+
+        self.use_ntvfs = "smb" in self.lp.get("server services")
+
+    def getntacl(self, path, as_sddl=False, direct_db_access=None):
+        if direct_db_access is None:
+            direct_db_access = self.use_ntvfs
+
+        ntacl_sd = getntacl(
+            self.lp, path,
+            direct_db_access=direct_db_access,
+            service=self.service)
+
+        return ntacl_sd.as_sddl(self.dom_sid) if as_sddl else ntacl_sd
+
+    def setntacl(self, path, ntacl_sd):
+        # ntacl_sd can be obj or str
+        return setntacl(self.lp, path, ntacl_sd, self.dom_sid)
+
+
+def _create_ntacl_file(dst, ntacl_sddl_str):
+    with open(dst + '.NTACL', 'w') as f:
+        f.write(ntacl_sddl_str)
+
+
+def _read_ntacl_file(src):
+    with open(src + '.NTACL', 'r') as f:
+        return f.read()
+
+
+def backup_online(smb_conn, dest_tarfile_path, dom_sid):
+    """
+    Backup all files and dirs with ntacl for the serive behind smb_conn.
+
+    1. Create a temp dir as container dir
+    2. Backup all files with dir structure into container dir
+    3. Generate file.NTACL files for each file and dir in contianer dir
+    4. Create a tar file from container dir(without top level folder)
+    5. Delete contianer dir
+    """
+
+    if isinstance(dom_sid, str):
+        dom_sid = security.dom_sid(dom_sid)
+
+    smb_helper = SMBHelper(smb_conn, dom_sid)
+
+    remotedir = ''  # root dir
+
+    localdir = tempfile.mkdtemp()
+
+    r_dirs = [remotedir]
+    l_dirs = [localdir]
+
+    while r_dirs:
+        r_dir = r_dirs.pop()
+        l_dir = l_dirs.pop()
+
+        for e in smb_helper.list(smb_path=r_dir):
+            r_name = smb_helper.join(r_dir, e['name'])
+            l_name = os.path.join(l_dir, e['name'])
+
+            if smb_helper.is_dir(e['attrib']):
+                r_dirs.append(r_name)
+                l_dirs.append(l_name)
+                os.mkdir(l_name)
+            else:
+                data = smb_helper.loadfile(r_name)
+                with open(l_name, 'wb') as f:
+                    f.write(data)
+
+            # get ntacl for this entry and save alongside
+            ntacl_sddl_str = smb_helper.get_acl(r_name, as_sddl=True)
+            _create_ntacl_file(l_name, ntacl_sddl_str)
+
+    with tarfile.open(name=dest_tarfile_path, mode='w:gz') as tar:
+        for name in os.listdir(localdir):
+            path = os.path.join(localdir, name)
+            tar.add(path, arcname=name)
+
+    shutil.rmtree(localdir)
+
+
+def backup_offline(src_service_path, dest_tarfile_path, samdb_conn, smb_conf_path):
+    """
+    Backup files and ntacls to a tarfile for a service
+    """
+    service = src_service_path.rstrip('/').rsplit('/', 1)[-1]
+    tempdir = tempfile.mkdtemp()
+
+    dom_sid_str = samdb_conn.get_domain_sid()
+    dom_sid = security.dom_sid(dom_sid_str)
+
+    ntacls_helper = NtaclsHelper(service, smb_conf_path, dom_sid)
+
+    for dirpath, dirnames, filenames in os.walk(src_service_path):
+        # each dir only cares about its direct children
+        rel_dirpath = os.path.relpath(dirpath, start=src_service_path)
+        dst_dirpath = os.path.join(tempdir, rel_dirpath)
+
+        # create sub dirs and NTACL file
+        for dirname in dirnames:
+            src = os.path.join(dirpath, dirname)
+            dst = os.path.join(dst_dirpath, dirname)
+            # mkdir with metadata
+            smbd.mkdir(dst, service)
+            ntacl_sddl_str = ntacls_helper.getntacl(src, as_sddl=True)
+            _create_ntacl_file(dst, ntacl_sddl_str)
+
+        # create files and NTACL file, then copy data
+        for filename in filenames:
+            src = os.path.join(dirpath, filename)
+            dst = os.path.join(dst_dirpath, filename)
+            # create an empty file with metadata
+            smbd.create_file(dst, service)
+            ntacl_sddl_str = ntacls_helper.getntacl(src, as_sddl=True)
+            _create_ntacl_file(dst, ntacl_sddl_str)
+
+            # now put data in
+            with open(src, 'rb') as src_file:
+                data = src_file.read()
+                with open(dst, 'wb') as dst_file:
+                    dst_file.write(data)
+
+    # add all files in tempdir to tarfile without a top folder
+    with tarfile.open(name=dest_tarfile_path, mode='w:gz') as tar:
+        for name in os.listdir(tempdir):
+            path = os.path.join(tempdir, name)
+            tar.add(path, arcname=name)
+
+    shutil.rmtree(tempdir)
+
+
+def backup_restore(src_tarfile_path, dst_service_path, samdb_conn, smb_conf_path):
+    """
+    Restore files and ntacls from a tarfile to a service
+    """
+    service = dst_service_path.rstrip('/').rsplit('/', 1)[-1]
+    tempdir = tempfile.mkdtemp()  # src files
+
+    dom_sid_str = samdb_conn.get_domain_sid()
+    dom_sid = security.dom_sid(dom_sid_str)
+
+    ntacls_helper = NtaclsHelper(service, smb_conf_path, dom_sid)
+
+    with tarfile.open(src_tarfile_path) as f:
+        f.extractall(path=tempdir)
+        # e.g.: /tmp/tmpRNystY/{dir1,dir1.NTACL,...file1,file1.NTACL}
+
+    for dirpath, dirnames, filenames in os.walk(tempdir):
+        rel_dirpath = os.path.relpath(dirpath, start=tempdir)
+        dst_dirpath = os.path.normpath(
+            os.path.join(dst_service_path, rel_dirpath))
+
+        for dirname in dirnames:
+            if not dirname.endswith('.NTACL'):
+                src = os.path.join(dirpath, dirname)
+                dst = os.path.join(dst_dirpath, dirname)
+                if not os.path.isdir(dst):
+                    # dst must be absolute path for smbd API
+                    smbd.mkdir(dst, service)
+                ntacl_sddl_str = _read_ntacl_file(src)
+                ntacls_helper.setntacl(dst, ntacl_sddl_str)
+
+        for filename in filenames:
+            if not filename.endswith('.NTACL'):
+                src = os.path.join(dirpath, filename)
+                dst = os.path.join(dst_dirpath, filename)
+                if not os.path.isfile(dst):
+                    # dst must be absolute path for smbd API
+                    smbd.create_file(dst, service)
+                ntacl_sddl_str = _read_ntacl_file(src)
+                ntacls_helper.setntacl(dst, ntacl_sddl_str)
+
+                # now put data in
+                with open(src, 'rb') as src_file:
+                    data = src_file.read()
+                    with open(dst, 'wb') as dst_file:
+                        dst_file.write(data)
+
+    shutil.rmtree(tempdir)
-- 
2.11.0


From 8e38bb148994f5dbe395406638986ea3c58a3b15 Mon Sep 17 00:00:00 2001
From: Joe Guo <joeg at catalyst.net.nz>
Date: Thu, 28 Jun 2018 10:45:28 +1200
Subject: [PATCH 6/9] ntacls: add extra test file for ntacls backup

The ntacls backup tests have to run in ad_dc:local env, which is
different from existing ntacls tests. Add a separate file for backup
tests.

Signed-off-by: Joe Guo <joeg at catalyst.net.nz>
---
 python/samba/tests/ntacls_backup.py | 185 ++++++++++++++++++++++++++++++++++++
 1 file changed, 185 insertions(+)
 create mode 100644 python/samba/tests/ntacls_backup.py

diff --git a/python/samba/tests/ntacls_backup.py b/python/samba/tests/ntacls_backup.py
new file mode 100644
index 00000000000..9ab264a27fd
--- /dev/null
+++ b/python/samba/tests/ntacls_backup.py
@@ -0,0 +1,185 @@
+# Unix SMB/CIFS implementation. Tests for ntacls manipulation
+# Copyright (C) Andrew Bartlett 2018
+# Copyright (C) Joe Guo <joeg at catalyst.net.nz> 2018
+#
+# 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/>.
+#
+
+"""Tests for samba ntacls backup"""
+import os
+
+from samba import smb
+from samba.samba3 import smbd
+from samba import samdb
+from samba import ntacls
+
+from samba.auth import system_session
+from samba.param import LoadParm
+from samba.dcerpc import security
+from samba.tests import TestCaseInTempDir
+
+
+class NtaclsBackupRestoreTests(TestCaseInTempDir):
+    """
+    Tests for NTACLs backup and restore.
+    """
+
+    def setUp(self):
+        super(NtaclsBackupRestoreTests, self).setUp()
+
+        self.server = os.environ["SERVER"]  # addc
+        samdb_url='ldap://' + self.server
+
+        self.service = 'test1'  # service/share to test
+        # root path for service
+        self.service_root = os.path.join(
+            os.environ["LOCAL_PATH"], self.service)
+
+        self.smb_conf_path = os.environ['SMB_CONF_PATH']
+        self.dom_sid = security.dom_sid(os.environ['DOMSID'])
+
+        self.creds = self.insta_creds(template=self.get_credentials())
+
+        # helper will load conf into lp, that's how smbd can find services.
+        self.ntacls_helper = ntacls.NtaclsHelper(self.service,
+                                                 self.smb_conf_path,
+                                                 self.dom_sid)
+
+        self.lp = self.ntacls_helper.lp
+
+        self.samdb_conn = samdb.SamDB(
+            url=samdb_url, session_info=system_session(),
+            credentials=self.creds, lp=self.lp)
+
+        self.smb_conn = smb.SMB(
+            self.server, self.service, lp=self.lp, creds=self.creds)
+
+        self.smb_helper = ntacls.SMBHelper(self.smb_conn, self.dom_sid)
+
+        self.tarfile_path = os.path.join(self.tempdir,
+                                         'ntacls-backup.tar.gz')
+
+        # an example file tree
+        self.tree = {
+            'file0.txt': b'test file0',
+            'dir1': {
+                'file1.txt': b'test file1',
+                'dir2': {}  # an empty dir in dir
+            },
+        }
+
+        self._delete_tarfile()
+        self.smb_helper.delete_tree()
+
+        self.smb_helper.create_tree(self.tree)
+        self._check_tree()
+        # keep a copy of ntacls after tree just created
+        self.original_ntacls = self.smb_helper.get_ntacls()
+
+    def tearDown(self):
+        self._delete_tarfile()
+        self.smb_helper.delete_tree()
+        super(NtaclsBackupRestoreTests, self).tearDown()
+
+    def _delete_tarfile(self):
+        try:
+            os.remove(self.tarfile_path)
+        except OSError:
+            pass
+
+    def _check_tarfile(self):
+        self.assertTrue(os.path.isfile(self.tarfile_path))
+
+    def _check_tree(self):
+        actual_tree = self.smb_helper.get_tree()
+        self.assertDictEqual(self.tree, actual_tree)
+
+    def test_smbd_mkdir(self):
+        """
+        A smoke test for smbd.mkdir API
+        """
+
+        dirpath = os.path.join(self.service_root, 'a-dir')
+        smbd.mkdir(dirpath, self.service)
+        self.assertTrue(os.path.isdir(dirpath))
+
+    def test_smbd_create_file(self):
+        """
+        A smoke test for smbd.create_file and smbd.unlink API
+        """
+
+        filepath = os.path.join(self.service_root, 'a-file')
+        smbd.create_file(filepath, self.service)
+        self.assertTrue(os.path.isfile(filepath))
+
+        # As well as checking that unlink works, this removes the
+        # fake xattrs from the dev/inode based DB
+        smbd.unlink(filepath, self.service)
+        self.assertFalse(os.path.isfile(filepath))
+
+    def test_compare_getntacl(self):
+        """
+        Ntacls get from different ways should be the same
+        """
+
+        file_name = 'file0.txt'
+        file_path = os.path.join(self.service_root, file_name)
+
+        sd0 = self.smb_helper.get_acl(file_name, as_sddl=True)
+
+        sd1 = self.ntacls_helper.getntacl(
+            file_path, as_sddl=True, direct_db_access=False)
+
+        sd2 = self.ntacls_helper.getntacl(
+            file_path, as_sddl=True, direct_db_access=True)
+
+        self.assertEquals(sd0, sd1)
+        self.assertEquals(sd1, sd2)
+
+    def test_backup_online(self):
+        """
+        Backup service online, delete files, restore and check.
+        """
+        ntacls.backup_online(
+            self.smb_conn, self.tarfile_path, self.dom_sid)
+        self._check_tarfile()
+
+        self.smb_helper.delete_tree()
+        ntacls.backup_restore(
+            self.tarfile_path, self.service_root,
+            self.samdb_conn, self.smb_conf_path)
+        self._check_tree()
+
+        # compare ntacls after restored
+        self.assertDictEqual(
+            self.original_ntacls, self.smb_helper.get_ntacls())
+
+    def test_backup_offline(self):
+        """
+        Backup service offline, delete files, restore and check.
+        """
+        ntacls.backup_offline(
+            self.service_root, self.tarfile_path,
+            self.samdb_conn, self.smb_conf_path)
+        self._check_tarfile()
+
+        self.smb_helper.delete_tree()
+        ntacls.backup_restore(
+            self.tarfile_path, self.service_root,
+            self.samdb_conn, self.smb_conf_path)
+        self._check_tree()
+
+        # compare ntacls after restored
+        self.assertDictEqual(
+            self.original_ntacls, self.smb_helper.get_ntacls())
-- 
2.11.0


From 43de42489b1abb33d541a34b982e363d2d38cb45 Mon Sep 17 00:00:00 2001
From: Joe Guo <joeg at catalyst.net.nz>
Date: Thu, 28 Jun 2018 10:47:42 +1200
Subject: [PATCH 7/9] tests/ntacls_backup: register test

Signed-off-by: Joe Guo <joeg at catalyst.net.nz>
---
 source4/selftest/tests.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
index 1b592c3b0f1..2551603d52b 100755
--- a/source4/selftest/tests.py
+++ b/source4/selftest/tests.py
@@ -641,6 +641,10 @@ planoldpythontestsuite(
     "ad_dc_ntvfs:local", "samba.tests.dcerpc.registry",
     extra_args=['-U"$USERNAME%$PASSWORD"'], py3_compatible=True)
 
+planoldpythontestsuite(
+    "ad_dc:local", "samba.tests.ntacls_backup",
+    extra_args=['-U"$USERNAME%$PASSWORD"'], py3_compatible=True)
+
 planoldpythontestsuite("ad_dc_ntvfs", "samba.tests.dcerpc.dnsserver", extra_args=['-U"$USERNAME%$PASSWORD"'])
 planoldpythontestsuite("ad_dc", "samba.tests.dcerpc.dnsserver", extra_args=['-U"$USERNAME%$PASSWORD"'])
 planoldpythontestsuite("chgdcpass", "samba.tests.dcerpc.raw_protocol", extra_args=['-U"$USERNAME%$PASSWORD"'])
-- 
2.11.0


From 5e7c8b1fa07216d0703d069b02ecbb14013fb44e Mon Sep 17 00:00:00 2001
From: Joe Guo <joeg at catalyst.net.nz>
Date: Fri, 1 Jun 2018 14:23:54 +1200
Subject: [PATCH 8/9] tests/ntacls: use global vars to make code DRY

Move acl and dommain_sid to global vars so we don't repeat them in every
test.

Signed-off-by: Joe Guo <joeg at catalyst.net.nz>
---
 python/samba/tests/ntacls.py | 26 ++++++++++++--------------
 1 file changed, 12 insertions(+), 14 deletions(-)

diff --git a/python/samba/tests/ntacls.py b/python/samba/tests/ntacls.py
index 8cd09fbcc54..b01e0b1aeb8 100644
--- a/python/samba/tests/ntacls.py
+++ b/python/samba/tests/ntacls.py
@@ -24,53 +24,51 @@ from samba.dcerpc import security
 from samba.tests import TestCaseInTempDir, SkipTest
 import os
 
+NTACL_SDDL = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
+DOMAIN_SID = "S-1-5-21-2212615479-2695158682-2101375467"
+
+
 class NtaclsTests(TestCaseInTempDir):
 
     def test_setntacl(self):
         lp = LoadParm()
-        acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
         open(self.tempf, 'w').write("empty")
         lp.set("posix:eadb",os.path.join(self.tempdir,"eadbtest.tdb"))
-        setntacl(lp, self.tempf, acl, "S-1-5-21-2212615479-2695158682-2101375467")
+        setntacl(lp, self.tempf, NTACL_SDDL, DOMAIN_SID)
         os.unlink(os.path.join(self.tempdir,"eadbtest.tdb"))
 
     def test_setntacl_getntacl(self):
         lp = LoadParm()
-        acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
         open(self.tempf, 'w').write("empty")
         lp.set("posix:eadb",os.path.join(self.tempdir,"eadbtest.tdb"))
-        setntacl(lp,self.tempf,acl,"S-1-5-21-2212615479-2695158682-2101375467")
+        setntacl(lp, self.tempf, NTACL_SDDL, DOMAIN_SID)
         facl = getntacl(lp,self.tempf)
         anysid = security.dom_sid(security.SID_NT_SELF)
-        self.assertEquals(facl.as_sddl(anysid),acl)
+        self.assertEquals(facl.as_sddl(anysid), NTACL_SDDL)
         os.unlink(os.path.join(self.tempdir,"eadbtest.tdb"))
 
     def test_setntacl_getntacl_param(self):
         lp = LoadParm()
-        acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
         open(self.tempf, 'w').write("empty")
-        setntacl(lp,self.tempf,acl,"S-1-5-21-2212615479-2695158682-2101375467","tdb",os.path.join(self.tempdir,"eadbtest.tdb"))
+        setntacl(lp, self.tempf, NTACL_SDDL, DOMAIN_SID,"tdb", os.path.join(self.tempdir,"eadbtest.tdb"))
         facl=getntacl(lp,self.tempf,"tdb",os.path.join(self.tempdir,"eadbtest.tdb"))
         domsid=security.dom_sid(security.SID_NT_SELF)
-        self.assertEquals(facl.as_sddl(domsid),acl)
+        self.assertEquals(facl.as_sddl(domsid), NTACL_SDDL)
         os.unlink(os.path.join(self.tempdir,"eadbtest.tdb"))
 
     def test_setntacl_invalidbackend(self):
         lp = LoadParm()
-        acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
         open(self.tempf, 'w').write("empty")
-        self.assertRaises(XattrBackendError, setntacl, lp, self.tempf, acl, "S-1-5-21-2212615479-2695158682-2101375467","ttdb", os.path.join(self.tempdir,"eadbtest.tdb"))
+        self.assertRaises(XattrBackendError, setntacl, lp, self.tempf, NTACL_SDDL, DOMAIN_SID, "ttdb", os.path.join(self.tempdir,"eadbtest.tdb"))
 
     def test_setntacl_forcenative(self):
         if os.getuid() == 0:
             raise SkipTest("Running test as root, test skipped")
         lp = LoadParm()
-        acl = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
         open(self.tempf, 'w').write("empty")
         lp.set("posix:eadb", os.path.join(self.tempdir,"eadbtest.tdb"))
-        self.assertRaises(Exception, setntacl, lp, self.tempf ,acl,
-            "S-1-5-21-2212615479-2695158682-2101375467","native")
-
+        self.assertRaises(Exception, setntacl, lp, self.tempf, NTACL_SDDL,
+            DOMAIN_SID, "native")
 
     def setUp(self):
         super(NtaclsTests, self).setUp()
-- 
2.11.0


From 987914f0129e8624208c7b5e157c5146ab7ac403 Mon Sep 17 00:00:00 2001
From: Joe Guo <joeg at catalyst.net.nz>
Date: Fri, 1 Jun 2018 14:28:43 +1200
Subject: [PATCH 9/9] tests/ntacls: fix pep8 warnings

Signed-off-by: Joe Guo <joeg at catalyst.net.nz>
---
 python/samba/tests/ntacls.py | 49 ++++++++++++++++++++++++--------------------
 1 file changed, 27 insertions(+), 22 deletions(-)

diff --git a/python/samba/tests/ntacls.py b/python/samba/tests/ntacls.py
index b01e0b1aeb8..b345b283a76 100644
--- a/python/samba/tests/ntacls.py
+++ b/python/samba/tests/ntacls.py
@@ -18,11 +18,12 @@
 
 """Tests for samba.ntacls."""
 
+import os
+
 from samba.ntacls import setntacl, getntacl, XattrBackendError
 from samba.param import LoadParm
 from samba.dcerpc import security
 from samba.tests import TestCaseInTempDir, SkipTest
-import os
 
 NTACL_SDDL = "O:S-1-5-21-2212615479-2695158682-2101375467-512G:S-1-5-21-2212615479-2695158682-2101375467-513D:(A;OICI;0x001f01ff;;;S-1-5-21-2212615479-2695158682-2101375467-512)"
 DOMAIN_SID = "S-1-5-21-2212615479-2695158682-2101375467"
@@ -30,51 +31,55 @@ DOMAIN_SID = "S-1-5-21-2212615479-2695158682-2101375467"
 
 class NtaclsTests(TestCaseInTempDir):
 
+    def setUp(self):
+        super(NtaclsTests, self).setUp()
+        self.tempf = os.path.join(self.tempdir, "test")
+        open(self.tempf, 'w').write("empty")
+
+    def tearDown(self):
+        os.unlink(self.tempf)
+        super(NtaclsTests, self).tearDown()
+
     def test_setntacl(self):
         lp = LoadParm()
         open(self.tempf, 'w').write("empty")
-        lp.set("posix:eadb",os.path.join(self.tempdir,"eadbtest.tdb"))
+        lp.set("posix:eadb", os.path.join(self.tempdir, "eadbtest.tdb"))
         setntacl(lp, self.tempf, NTACL_SDDL, DOMAIN_SID)
-        os.unlink(os.path.join(self.tempdir,"eadbtest.tdb"))
+        os.unlink(os.path.join(self.tempdir, "eadbtest.tdb"))
 
     def test_setntacl_getntacl(self):
         lp = LoadParm()
         open(self.tempf, 'w').write("empty")
-        lp.set("posix:eadb",os.path.join(self.tempdir,"eadbtest.tdb"))
+        lp.set("posix:eadb", os.path.join(self.tempdir, "eadbtest.tdb"))
         setntacl(lp, self.tempf, NTACL_SDDL, DOMAIN_SID)
-        facl = getntacl(lp,self.tempf)
+        facl = getntacl(lp, self.tempf)
         anysid = security.dom_sid(security.SID_NT_SELF)
         self.assertEquals(facl.as_sddl(anysid), NTACL_SDDL)
-        os.unlink(os.path.join(self.tempdir,"eadbtest.tdb"))
+        os.unlink(os.path.join(self.tempdir, "eadbtest.tdb"))
 
     def test_setntacl_getntacl_param(self):
         lp = LoadParm()
         open(self.tempf, 'w').write("empty")
-        setntacl(lp, self.tempf, NTACL_SDDL, DOMAIN_SID,"tdb", os.path.join(self.tempdir,"eadbtest.tdb"))
-        facl=getntacl(lp,self.tempf,"tdb",os.path.join(self.tempdir,"eadbtest.tdb"))
-        domsid=security.dom_sid(security.SID_NT_SELF)
+        setntacl(lp, self.tempf, NTACL_SDDL, DOMAIN_SID, "tdb",
+                 os.path.join(self.tempdir, "eadbtest.tdb"))
+        facl = getntacl(lp, self.tempf, "tdb", os.path.join(
+            self.tempdir, "eadbtest.tdb"))
+        domsid = security.dom_sid(security.SID_NT_SELF)
         self.assertEquals(facl.as_sddl(domsid), NTACL_SDDL)
-        os.unlink(os.path.join(self.tempdir,"eadbtest.tdb"))
+        os.unlink(os.path.join(self.tempdir, "eadbtest.tdb"))
 
     def test_setntacl_invalidbackend(self):
         lp = LoadParm()
         open(self.tempf, 'w').write("empty")
-        self.assertRaises(XattrBackendError, setntacl, lp, self.tempf, NTACL_SDDL, DOMAIN_SID, "ttdb", os.path.join(self.tempdir,"eadbtest.tdb"))
+        self.assertRaises(XattrBackendError, setntacl, lp, self.tempf,
+                          NTACL_SDDL, DOMAIN_SID, "ttdb",
+                          os.path.join(self.tempdir, "eadbtest.tdb"))
 
     def test_setntacl_forcenative(self):
         if os.getuid() == 0:
             raise SkipTest("Running test as root, test skipped")
         lp = LoadParm()
         open(self.tempf, 'w').write("empty")
-        lp.set("posix:eadb", os.path.join(self.tempdir,"eadbtest.tdb"))
+        lp.set("posix:eadb", os.path.join(self.tempdir, "eadbtest.tdb"))
         self.assertRaises(Exception, setntacl, lp, self.tempf, NTACL_SDDL,
-            DOMAIN_SID, "native")
-
-    def setUp(self):
-        super(NtaclsTests, self).setUp()
-        self.tempf = os.path.join(self.tempdir, "test")
-        open(self.tempf, 'w').write("empty")
-
-    def tearDown(self):
-        os.unlink(self.tempf)
-        super(NtaclsTests, self).tearDown()
+                          DOMAIN_SID, "native")
-- 
2.11.0



More information about the samba-technical mailing list