[RFC] vfs_fruit uses wrong xattr name for Mac metadata on FreeBSD

Ralph Böhme slow at samba.org
Sat Feb 4 09:17:17 UTC 2017


On Sun, Jan 22, 2017 at 05:08:08PM +0100, Ralph Böhme wrote:
> On Sun, Jan 22, 2017 at 06:48:03AM +0800, Evan Champion wrote:
> > Another way would be to change the default xattr name in 4.6.0 and provide a
> > conversion tool.
> 
> hm...

and here's the patchset that does this.

1. It renames the Netatalk metadata xattr name.

2. Adds a tool mvxattr to rename xattrs. The name seems not to be in use, so
hopefully it doesn't clash with anything. It uses nftw under the hood for the
directory traversal, it's in POSIX.1-2001, available on FreeBSD (which this is
all about, Linux has it as well) and I'd like to avoid reinventing the wheel.

3. Fixes our libreplace xattr implementation on FreeBSD to validata xattr
names. This is a prerequisite for 2, otherwise users might call the mvxattr tool
with xattr name like "org.netatalk.Metadata" without a namespace prefix leading
to unexpected results: the xattr name on the fs would be "netatalk.Metadata" as
the "org" part gets stripped.

Once this is in master, I'll prepare a backport for 4.6 with an update for
WHATSNEW.

Please review & push if happy. Thanks!

Cheerio!
-slow
-------------- next part --------------
From e661ceb3f27582b0c699584f116d51787db3e2b2 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 3 Feb 2017 16:33:00 +0100
Subject: [PATCH 1/4] vfs_fruit: correct Netatalk metadata xattr on FreeBSD

Bug: https://bugzilla.samba.org/show_bug.cgi?id=12490

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 3599dcb..828d6d1 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -105,7 +105,7 @@ static int vfs_fruit_debug_level = DBGC_VFS;
  * This is hokey, but what else can we do?
  */
 #define NETATALK_META_XATTR "org.netatalk.Metadata"
-#if defined(HAVE_ATTROPEN) || defined(FREEBSD)
+#if defined(HAVE_ATTROPEN)
 #define AFPINFO_EA_NETATALK NETATALK_META_XATTR
 #define AFPRESOURCE_EA_NETATALK "org.netatalk.ResourceFork"
 #else
-- 
2.9.3


From 1fe44e3f5b2bdff92e4c7267e07b69733fbf4758 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 3 Feb 2017 16:43:26 +0100
Subject: [PATCH 2/4] vfs_fruit: cleanup metadata and resource xattr name
 defines

Just some cleanup, no change in behaviour. This also removes the hokey
tag. :)

Bug: https://bugzilla.samba.org/show_bug.cgi?id=12490

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 source3/modules/vfs_fruit.c | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
index 828d6d1..4387447 100644
--- a/source3/modules/vfs_fruit.c
+++ b/source3/modules/vfs_fruit.c
@@ -100,17 +100,15 @@ static int vfs_fruit_debug_level = DBGC_VFS;
 #define FRUIT_PARAM_TYPE_NAME "fruit"
 #define ADOUBLE_NAME_PREFIX "._"
 
-/*
- * REVIEW:
- * This is hokey, but what else can we do?
- */
 #define NETATALK_META_XATTR "org.netatalk.Metadata"
+#define NETATALK_RSRC_XATTR "org.netatalk.Metadata"
+
 #if defined(HAVE_ATTROPEN)
 #define AFPINFO_EA_NETATALK NETATALK_META_XATTR
-#define AFPRESOURCE_EA_NETATALK "org.netatalk.ResourceFork"
+#define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
 #else
 #define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
-#define AFPRESOURCE_EA_NETATALK "user.org.netatalk.ResourceFork"
+#define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
 #endif
 
 enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
-- 
2.9.3


From 03d73c0da1955aa703c11e79ff23fd97246d3e79 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 3 Feb 2017 18:08:12 +0100
Subject: [PATCH 3/4] lib/replace: validate xattr namespace prefix on FreeBSD

We should validate the xattr name string ensuring it either begins with
"sytem." or "user.". If it doesn't, we should fail the request with
EINVAL.

The FreeBSD xattr API uses namespaces but doesn't put the namespace name
as a string prefix at the beginning of the xattr name. It gets passed as
an additional int arg instead.

On the other hand, our libreplace xattr API expects the caller to put a
namespace prefix into the xattr name.

Unfortunately the conversion and stripping of the namespae string prefix
from the xattr name gives the following unexpected result on FreeBSD:

rep_setxattr("foo.bar", ...) => xattr with name "bar"

The code checks if the name begins with "system.", if it doesn't find
it, it defaults to the user namespace and then does a strchr(name, '.')
which skips *any* leading string before the first dot.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=12490

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 lib/replace/xattr.c | 105 ++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 81 insertions(+), 24 deletions(-)

diff --git a/lib/replace/xattr.c b/lib/replace/xattr.c
index ce52d1a..2479c21 100644
--- a/lib/replace/xattr.c
+++ b/lib/replace/xattr.c
@@ -61,11 +61,21 @@ ssize_t rep_getxattr (const char *path, const char *name, void *value, size_t si
 #elif defined(HAVE_GETEA)
 	return getea(path, name, value, size);
 #elif defined(HAVE_EXTATTR_GET_FILE)
-	char *s;
 	ssize_t retval;
-	int attrnamespace = (strncmp(name, "system", 6) == 0) ? 
-		EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
-	const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
+	int attrnamespace;
+	const char *attrname;
+
+	if (strncmp(name, "system.", 7) == 0) {
+		attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
+		attrname = name + 7;
+	} else if (strncmp(name, "user.", 5) == 0) {
+		attrnamespace = EXTATTR_NAMESPACE_USER;
+		attrname = name + 5;
+	} else {
+		errno = EINVAL;
+		return -1;
+	}
+
 	/*
 	 * The BSD implementation has a nasty habit of silently truncating
 	 * the returned value to the size of the buffer, so we have to check
@@ -125,11 +135,20 @@ ssize_t rep_fgetxattr (int filedes, const char *name, void *value, size_t size)
 #elif defined(HAVE_FGETEA)
 	return fgetea(filedes, name, value, size);
 #elif defined(HAVE_EXTATTR_GET_FD)
-	char *s;
 	ssize_t retval;
-	int attrnamespace = (strncmp(name, "system", 6) == 0) ? 
-		EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
-	const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
+	int attrnamespace;
+	const char *attrname;
+
+	if (strncmp(name, "system.", 7) == 0) {
+		attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
+		attrname = name + 7;
+	} else if (strncmp(name, "user.", 5) == 0) {
+		attrnamespace = EXTATTR_NAMESPACE_USER;
+		attrname = name + 5;
+	} else {
+		errno = EINVAL;
+		return -1;
+	}
 
 	if((retval=extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0)) >= 0) {
 		if (size == 0) {
@@ -414,10 +433,19 @@ int rep_removexattr (const char *path, const char *name)
 #elif defined(HAVE_REMOVEEA)
 	return removeea(path, name);
 #elif defined(HAVE_EXTATTR_DELETE_FILE)
-	char *s;
-	int attrnamespace = (strncmp(name, "system", 6) == 0) ? 
-		EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
-	const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
+	int attrnamespace;
+	const char *attrname;
+
+	if (strncmp(name, "system.", 7) == 0) {
+		attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
+		attrname = name + 7;
+	} else if (strncmp(name, "user.", 5) == 0) {
+		attrnamespace = EXTATTR_NAMESPACE_USER;
+		attrname = name + 5;
+	} else {
+		errno = EINVAL;
+		return -1;
+	}
 
 	return extattr_delete_file(path, attrnamespace, attrname);
 #elif defined(HAVE_ATTR_REMOVE)
@@ -455,10 +483,19 @@ int rep_fremovexattr (int filedes, const char *name)
 #elif defined(HAVE_FREMOVEEA)
 	return fremoveea(filedes, name);
 #elif defined(HAVE_EXTATTR_DELETE_FD)
-	char *s;
-	int attrnamespace = (strncmp(name, "system", 6) == 0) ? 
-		EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
-	const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
+	int attrnamespace;
+	const char *attrname;
+
+	if (strncmp(name, "system.", 7) == 0) {
+		attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
+		attrname = name + 7;
+	} else if (strncmp(name, "user.", 5) == 0) {
+		attrnamespace = EXTATTR_NAMESPACE_USER;
+		attrname = name + 5;
+	} else {
+		errno = EINVAL;
+		return -1;
+	}
 
 	return extattr_delete_fd(filedes, attrnamespace, attrname);
 #elif defined(HAVE_ATTR_REMOVEF)
@@ -496,11 +533,21 @@ int rep_setxattr (const char *path, const char *name, const void *value, size_t
 #elif defined(HAVE_SETEA)
 	return setea(path, name, value, size, flags);
 #elif defined(HAVE_EXTATTR_SET_FILE)
-	char *s;
 	int retval = 0;
-	int attrnamespace = (strncmp(name, "system", 6) == 0) ? 
-		EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
-	const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
+	int attrnamespace;
+	const char *attrname;
+
+	if (strncmp(name, "system.", 7) == 0) {
+		attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
+		attrname = name + 7;
+	} else if (strncmp(name, "user.", 5) == 0) {
+		attrnamespace = EXTATTR_NAMESPACE_USER;
+		attrname = name + 5;
+	} else {
+		errno = EINVAL;
+		return -1;
+	}
+
 	if (flags) {
 		/* Check attribute existence */
 		retval = extattr_get_file(path, attrnamespace, attrname, NULL, 0);
@@ -563,11 +610,21 @@ int rep_fsetxattr (int filedes, const char *name, const void *value, size_t size
 #elif defined(HAVE_FSETEA)
 	return fsetea(filedes, name, value, size, flags);
 #elif defined(HAVE_EXTATTR_SET_FD)
-	char *s;
 	int retval = 0;
-	int attrnamespace = (strncmp(name, "system", 6) == 0) ? 
-		EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
-	const char *attrname = ((s=strchr(name, '.')) == NULL) ? name : s + 1;
+	int attrnamespace;
+	const char *attrname;
+
+	if (strncmp(name, "system.", 7) == 0) {
+		attrnamespace = EXTATTR_NAMESPACE_SYSTEM;
+		attrname = name + 7;
+	} else if (strncmp(name, "user.", 5) == 0) {
+		attrnamespace = EXTATTR_NAMESPACE_USER;
+		attrname = name + 5;
+	} else {
+		errno = EINVAL;
+		return -1;
+	}
+
 	if (flags) {
 		/* Check attribute existence */
 		retval = extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0);
-- 
2.9.3


From 6a5867a6938926e355ff3bfba566af227e6c5c92 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow at samba.org>
Date: Fri, 3 Feb 2017 14:57:45 +0100
Subject: [PATCH 4/4] s3/util: mvxattr, a tool to rename extended attributes

Usage: mvxattr -s STRING -d STRING PATH [PATH ...]
  -s, --from=STRING         xattr source name
  -d, --to=STRING           xattr destination name
  -l, --follow-symlinks     follow symlinks, the default is to ignore them
  -p, --print               print files where the xattr got renamed
  -v, --verbose             print files as they are checked
  -f, --force               force overwriting of destination xattr

Help options:
  -?, --help            Show this help message
  --usage               Display brief usage message

Bug: https://bugzilla.samba.org/show_bug.cgi?id=12490

Signed-off-by: Ralph Boehme <slow at samba.org>
---
 docs-xml/manpages/mvxattr.1.xml | 100 ++++++++++++++++++++++
 docs-xml/wscript_build          |   1 +
 source3/utils/mvxattr.c         | 178 ++++++++++++++++++++++++++++++++++++++++
 source3/utils/wscript_build     |   8 ++
 source3/wscript                 |   3 +
 5 files changed, 290 insertions(+)
 create mode 100644 docs-xml/manpages/mvxattr.1.xml
 create mode 100644 source3/utils/mvxattr.c

diff --git a/docs-xml/manpages/mvxattr.1.xml b/docs-xml/manpages/mvxattr.1.xml
new file mode 100644
index 0000000..034dc3a
--- /dev/null
+++ b/docs-xml/manpages/mvxattr.1.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">
+<refentry id="mvxattr.1">
+
+<refmeta>
+	<refentrytitle>mvxattr</refentrytitle>
+	<manvolnum>1</manvolnum>
+	<refmiscinfo class="source">Samba</refmiscinfo>
+	<refmiscinfo class="manual">User Commands</refmiscinfo>
+	<refmiscinfo class="version">4.7</refmiscinfo>
+</refmeta>
+
+
+<refnamediv>
+	<refname>mvxattr</refname>
+	<refpurpose>Recursively rename extended attributes</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+	<cmdsynopsis>
+		<command>mvxattr</command>
+		<arg choice="req">-s STRING, --from=STRING</arg>
+		<arg choice="req">-d STRING, --to=STRING</arg>
+		<arg choice="opt">-l, --follow-symlinks</arg>
+		<arg choice="opt">-p, --print</arg>
+		<arg choice="opt">-v, --verbose</arg>
+		<arg choice="opt">-f, --force</arg>
+		<arg choice="req">PATH [PATH ...]</arg>
+	</cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+	<title>DESCRIPTION</title>
+
+	<para>This tool is part of the <citerefentry><refentrytitle>samba</refentrytitle>
+	<manvolnum>1</manvolnum></citerefentry> suite.</para>
+
+	<para>mvxattr is a simple utility to recursively rename extended
+	attributes.</para>
+
+	<para>By default all symlinks are ignored, use <option>-l</option> to
+	follow them.</para>
+</refsect1>
+
+<refsect1>
+	<title>OPTIONS</title>
+
+	<variablelist>
+	<varlistentry>
+		<term>-s STRING, --from=STRING</term>
+		<listitem><para>Source xattr name</para></listitem>
+	</varlistentry>
+
+	<varlistentry>
+		<term>-d STRING, --to=STRING</term>
+		<listitem><para>Destination xattr name</para></listitem>
+	</varlistentry>
+
+	<varlistentry>
+		<term>-l, --follow-symlinks</term>
+		<listitem><para>Follow symlinks, the default is to ignore
+		them.</para></listitem>
+	</varlistentry>
+
+	<varlistentry>
+		<term>-p, --print</term>
+		<listitem><para>Print files where the xattr got renamed.</para></listitem>
+	</varlistentry>
+
+	<varlistentry>
+		<term>-v, --verbose</term>
+		<listitem><para>Print files as they are checked.</para></listitem>
+	</varlistentry>
+
+	<varlistentry>
+		<term>-f, --force</term>
+		<listitem><para>Force overwriting of destination xattr.</para></listitem>
+	</varlistentry>
+	</variablelist>
+
+</refsect1>
+
+<refsect1>
+	<title>VERSION</title>
+
+	<para>This man page is correct for version 4 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+	<title>AUTHOR</title>
+
+	<para>The original Samba software and related utilities were created by
+	Andrew Tridgell. Samba is now developed by the Samba Team as an Open
+	Source project similar to the way the Linux kernel is developed.</para>
+
+	<para>The mvxattr manpage was written by Ralph Boehme.</para>
+
+</refsect1>
+
+</refentry>
diff --git a/docs-xml/wscript_build b/docs-xml/wscript_build
index 2b3a180..0b690a8 100644
--- a/docs-xml/wscript_build
+++ b/docs-xml/wscript_build
@@ -18,6 +18,7 @@ manpages='''
          manpages/idmap_script.8
          manpages/idmap_tdb.8
          manpages/idmap_tdb2.8
+         manpages/mvxattr.1
          manpages/net.8
          manpages/nmbd.8
          manpages/nmblookup.1
diff --git a/source3/utils/mvxattr.c b/source3/utils/mvxattr.c
new file mode 100644
index 0000000..9cc2ec0
--- /dev/null
+++ b/source3/utils/mvxattr.c
@@ -0,0 +1,178 @@
+/*
+   Unix SMB/CIFS implementation.
+   xattr renaming
+   Copyright (C) Ralph Boehme 2017
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "popt_common.h"
+#include <ftw.h>
+
+static struct rename_xattr_state {
+	int follow_symlink;
+	int print;
+	int force;
+	int verbose;
+	char *xattr_from;
+	char *xattr_to;
+} state;
+
+static int rename_xattr(const char *path,
+			const struct stat *sb,
+			int typeflag,
+			struct FTW *ftwbuf)
+{
+	ssize_t len;
+	int ret;
+
+	if (typeflag == FTW_SL) {
+		d_printf("Ignoring symlink %s\n", path);
+		return 0;
+	}
+
+	if (state.verbose) {
+		d_printf("%s\n", path);
+	}
+
+	len = getxattr(path, state.xattr_from, NULL, 0);
+	if (len < 0) {
+		if (errno == ENOATTR) {
+			return 0;
+		}
+		d_printf("getxattr [%s] failed [%s]\n",
+			 path, strerror(errno));
+		return -1;
+	}
+
+	{
+		uint8_t buf[len];
+
+		len = getxattr(path, state.xattr_from, &buf[0], len);
+		if (len == -1) {
+			d_printf("getxattr [%s] failed [%s]\n",
+				 path, strerror(errno));
+			return -1;
+		}
+
+		ret = setxattr(path, state.xattr_to, &buf[0], len, XATTR_CREATE);
+		if (ret != 0) {
+			if (errno != EEXIST) {
+				d_printf("setxattr [%s] failed [%s]\n",
+					 path, strerror(errno));
+				return -1;
+			}
+			if (!state.force) {
+				d_printf("destination [%s:%s] exists, use -f to force\n",
+					 path, state.xattr_to);
+				return -1;
+			}
+			ret = setxattr(path, state.xattr_to, &buf[0], len, XATTR_REPLACE);
+			if (ret != 0) {
+				d_printf("setxattr [%s:%s] failed [%s]\n",
+					 path, state.xattr_to, strerror(errno));
+				return -1;
+			}
+		}
+
+		ret = removexattr(path, state.xattr_from);
+		if (ret != 0) {
+			d_printf("removexattr [%s:%s] failed [%s]\n",
+				 path, state.xattr_from, strerror(errno));
+			return -1;
+		}
+
+		if (state.print) {
+			d_printf("Renamed %s to %s on %s\n",
+				 state.xattr_from, state.xattr_to, path);
+		}
+	}
+
+	return 0;
+}
+
+int main(int argc, const char *argv[])
+{
+	int c;
+	const char *path = NULL;
+	poptContext pc;
+	struct poptOption long_options[] = {
+		POPT_AUTOHELP
+		{"from", 		's', POPT_ARG_STRING,	&state.xattr_from,	's', "xattr source name" },
+		{"to",			'd', POPT_ARG_STRING, 	&state.xattr_to, 	'd', "xattr destination name" },
+		{"follow-symlinks",	'l', POPT_ARG_NONE,	&state.follow_symlink,	'l', "follow symlinks, the default is to ignore them" },
+		{"print",		'p', POPT_ARG_NONE,	&state.print,		'p', "print files where the xattr got renamed" },
+		{"verbose",		'v', POPT_ARG_NONE,	&state.verbose,		'v', "print files as they are checked" },
+		{"force",		'f', POPT_ARG_NONE,	&state.force,		'f', "force overwriting of destination xattr" },
+		POPT_TABLEEND
+	};
+	TALLOC_CTX *frame = talloc_stackframe();
+	const char *s = NULL;
+	int ret = 0;
+
+	if (getuid() != 0) {
+		d_printf("%s only works as root!\n", argv[0]);
+		ret = 1;
+		goto done;
+	}
+
+	pc = poptGetContext(NULL, argc, argv, long_options, 0);
+	poptSetOtherOptionHelp(pc, "-s STRING -d STRING PATH [PATH ...]");
+
+	while ((c = poptGetNextOpt(pc)) != -1) {
+		switch (c) {
+		case 's':
+			s = poptGetOptArg(pc);
+			state.xattr_from = talloc_strdup(frame, s);
+			if (state.xattr_from == NULL) {
+				ret = 1;
+				goto done;
+			}
+			break;
+		case 'd':
+			s = poptGetOptArg(pc);
+			state.xattr_to = talloc_strdup(frame, s);
+			if (state.xattr_to == NULL) {
+				ret = 1;
+				goto done;
+			}
+			break;
+		}
+	}
+
+	if (state.xattr_from == NULL || state.xattr_to == NULL) {
+		poptPrintUsage(pc, stderr, 0);
+		ret = 1;
+		goto done;
+	}
+
+	if (poptPeekArg(pc) == NULL) {
+		poptPrintUsage(pc, stderr, 0);
+		ret = 1;
+		goto done;
+	}
+
+	while ((path = poptGetArg(pc)) != NULL) {
+		ret = nftw(path, rename_xattr, 256,
+			   state.follow_symlink ? 0 : FTW_PHYS);
+	}
+
+	poptFreeContext(pc);
+
+done:
+	TALLOC_FREE(frame);
+	return ret;
+}
diff --git a/source3/utils/wscript_build b/source3/utils/wscript_build
index 0b9356a..4295f85 100644
--- a/source3/utils/wscript_build
+++ b/source3/utils/wscript_build
@@ -249,3 +249,11 @@ bld.SAMBA3_BINARY('net',
                  trusts_util
                  IDMAP_AUTORID_TDB
                  ''')
+
+bld.SAMBA3_BINARY('mvxattr',
+                 source='mvxattr.c',
+                 deps='''
+                 talloc
+                 popt_samba3
+                 ''',
+                 enabled=bld.env.build_mvxattr)
diff --git a/source3/wscript b/source3/wscript
index 443affd..907e400 100644
--- a/source3/wscript
+++ b/source3/wscript
@@ -1633,6 +1633,9 @@ main() {
         else:
             Logs.info("ncurses not available, not building regedit")
 
+    if conf.CHECK_HEADERS('ftw.h') and conf.CHECK_FUNCS('nftw'):
+        conf.env.build_mvxattr = True
+
     conf.CHECK_FUNCS_IN('DES_pcbc_encrypt', 'crypto')
     if Options.options.with_fake_kaserver == True:
         conf.CHECK_HEADERS('afs/param.h afs/stds.h', together=True)
-- 
2.9.3



More information about the samba-technical mailing list