changeset 5064:0fbdaabf14a1

PSARC/2007/457 Winchester idmap(1M) update 6567797 ambiguity: "unixname" as an ID type for both Unix users and groups 6577598 idmap should return non-zero status on add/remove rule failures 6577601 idmap should pinpoint the exact entry from the batch on update rules failures 6579694 idmap should not display sid2pid fallback mapping if pid == -1 6601140 idmap remove winname:aaa should remove the group rule too 6599176 No strdup of idmap_utf8str_val
author dm199847
date Mon, 17 Sep 2007 08:07:28 -0700
parents c7cb857a0196
children fcf530c3356e
files usr/src/cmd/idmap/idmap/idmap.c usr/src/cmd/idmap/idmap/idmap_engine.c usr/src/cmd/idmap/idmap/idmap_engine.h usr/src/cmd/idmap/idmapd/dbutils.c usr/src/cmd/idmap/idmapd/idmapd.c usr/src/cmd/idmap/idmapd/idmapd.h usr/src/cmd/idmap/idmapd/rpc_svc.c usr/src/cmd/idmap/idmapd/server.c usr/src/head/rpcsvc/idmap_prot.x usr/src/lib/libidmap/common/idmap_api.c usr/src/lib/libidmap/common/idmap_impl.h usr/src/lib/libidmap/common/idmap_priv.h usr/src/lib/libidmap/common/mapfile-vers
diffstat 13 files changed, 1123 insertions(+), 743 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/idmap/idmap/idmap.c	Mon Sep 17 08:01:53 2007 -0700
+++ b/usr/src/cmd/idmap/idmap/idmap.c	Mon Sep 17 08:07:28 2007 -0700
@@ -31,6 +31,7 @@
 #include <strings.h>
 #include <errno.h>
 #include <limits.h>
+#include <sys/varargs.h>
 #include "idmap_engine.h"
 #include "idmap_priv.h"
 
@@ -46,13 +47,6 @@
 #define	I_NO 0
 #define	I_UNKNOWN -1
 
-/* Directions */
-
-#define	DIR_W2U 1
-#define	DIR_U2W 2
-#define	DIR_BI 0
-#define	DIR_UNKNOWN -1
-
 /*
  * used in do_show for the type of argument, which can be winname,
  * unixname, uid, gid, sid or not given at all:
@@ -60,28 +54,35 @@
 
 #define	TYPE_SID	0x010	/* sid */
 #define	TYPE_WN		0x110	/* winname */
+#define	TYPE_WU		0x111	/* winuser */
+#define	TYPE_WG		0x112	/* wingroup */
 #define	TYPE_UID	0x001	/* uid */
 #define	TYPE_GID	0x002	/* gid */
 #define	TYPE_PID	0x000	/* pid */
 #define	TYPE_UN		0x100	/* unixname */
+#define	TYPE_UU		0x101	/* unixuser */
+#define	TYPE_UG		0x102	/* unixgroup */
 
 #define	IS_WIN		0x010	/* mask for the windows types */
 #define	IS_NAME		0x100	/* mask for string name types */
-#define	IS_GROUP	0x002	/* mask for, well, TYPE_GID */
+#define	IS_USER		0x001	/* mask for user types */
+#define	IS_GROUP	0x002	/* mask for group types */
 
 
 /* Identity type strings */
 
 #define	ID_WINNAME	"winname"
 #define	ID_UNIXNAME	"unixname"
+#define	ID_UNIXUSER	"unixuser"
+#define	ID_UNIXGROUP	"unixgroup"
+#define	ID_WINUSER	"winuser"
+#define	ID_WINGROUP	"wingroup"
 #define	ID_SID	"sid"
 #define	ID_UID	"uid"
 #define	ID_GID	"gid"
 
 /* Flags */
 
-#define	g_FLAG	'g'
-#define	u_FLAG	'u'
 #define	f_FLAG	'f'
 #define	t_FLAG	't'
 #define	d_FLAG	'd'
@@ -116,8 +117,8 @@
  *
  * DEFAULT_FORMAT are in fact the idmap subcommands suitable for
  * piping to idmap standart input. For example
- * add -u -d winname:bob@foo.com unixname:fred
- * add -u -d winname:bob2bar.com unixname:fred
+ * add -d winuser:bob@foo.com unixuser:fred
+ * add -d winuser:bob2bar.com unixuser:fred
  *
  * SMBUSERS is the format of Samba username map (smbusers). For full
  * documentation, search for "username map" in smb.conf manpage.
@@ -175,6 +176,15 @@
 /* Are we in the batch mode? */
 static int batch_mode = 0;
 
+/* Self describing stricture for positions */
+struct pos_sds {
+	int size;
+	int last;
+	cmd_pos_t *pos[1];
+};
+
+static struct pos_sds *positions;
+
 /* Handles for idmap_api batch */
 static idmap_handle_t *handle = NULL;
 static idmap_udt_handle_t *udt = NULL;
@@ -184,15 +194,18 @@
 
 /* Command handlers */
 
-static int do_show_mapping(flag_t *f, int argc, char **argv);
-static int do_dump(flag_t *f, int argc, char **argv);
-static int do_import(flag_t *f, int argc, char **argv);
-static int do_list_name_mappings(flag_t *f, int argc, char **argv);
-static int do_add_name_mapping(flag_t *f, int argc, char **argv);
-static int do_remove_name_mapping(flag_t *f, int argc, char **argv);
-static int do_exit(flag_t *f, int argc, char **argv);
-static int do_export(flag_t *f, int argc, char **argv);
-static int do_help(flag_t *f, int argc, char **argv);
+static int do_show_mapping(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
+static int do_dump(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
+static int do_import(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
+static int do_list_name_mappings(flag_t *f, int argc, char **argv,
+    cmd_pos_t *pos);
+static int do_add_name_mapping(flag_t *f, int argc, char **argv,
+    cmd_pos_t *pos);
+static int do_remove_name_mapping(flag_t *f, int argc, char **argv,
+    cmd_pos_t *pos);
+static int do_exit(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
+static int do_export(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
+static int do_help(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
 
 /* Command names and their hanlers to be passed to idmap_engine */
 
@@ -204,7 +217,7 @@
 	},
 	{
 		"dump",
-		"n(names)g(group)u(user)",
+		"n(names)",
 		do_dump
 	},
 	{
@@ -219,17 +232,17 @@
 	},
 	{
 		"list",
-		"g(group)u(user)",
+		"",
 		do_list_name_mappings
 	},
 	{
 		"add",
-		"g(group)u(user)d(directional)",
+		"d(directional)",
 		do_add_name_mapping
 	},
 	{
 		"remove",
-		"a(all)u(user)g(group)t(to)f(from)d(directional)",
+		"a(all)t(to)f(from)d(directional)",
 		do_remove_name_mapping
 	},
 	{
@@ -244,6 +257,106 @@
 	}
 };
 
+/* Print error message, possibly with a position */
+/* printflike */
+static void
+print_error(cmd_pos_t *pos, const char *format, ...) {
+	size_t length;
+
+	va_list ap;
+
+	va_start(ap, format);
+
+	if (pos != NULL) {
+		length = strlen(pos->line);
+
+		/* Skip newlines etc at the end: */
+		while (length > 0 && isspace(pos->line[length - 1]))
+			length--;
+
+		(void) fprintf(stderr,
+		    gettext("Error at line %d: %.*s\n"),
+		    pos->linenum,
+		    length,
+		    pos->line);
+	}
+	(void) vfprintf(stderr, format, ap);
+
+	va_end(ap);
+}
+
+/* Inits positions sds. 0 means everything went OK, -1 for errors */
+static int
+init_positions() {
+	int init_size = 32; /* Initial size of the positions array */
+
+	positions = (struct pos_sds *) malloc(sizeof (struct pos_sds) +
+	    (init_size - 1) * sizeof (cmd_pos_t *));
+
+	if (positions == NULL) {
+		print_error(NULL, gettext("Not enough memory.\n"));
+		return (-1);
+	}
+
+	positions->size = init_size;
+	positions->last = 0;
+	return (0);
+}
+
+/* Free the positions array */
+static void
+fini_positions() {
+	int i;
+	for (i = 0; i < positions->last; i++) {
+		if (positions->pos[i] == NULL)
+			continue;
+		free(positions->pos[i]->line);
+		free(positions->pos[i]);
+	}
+	free(positions);
+
+	positions = NULL;
+}
+
+/*
+ * Add another position to the positions array. 0 means everything
+ * went OK, -1 for errors
+ */
+static int
+positions_add(cmd_pos_t *pos) {
+	if (positions->last >= positions->size) {
+		positions->size *= 2;
+		positions = (struct pos_sds *)realloc(positions,
+		    sizeof (struct pos_sds) +
+		    (positions->size - 1) * sizeof (cmd_pos_t *));
+		if (positions == NULL)
+			goto nomemory;
+	}
+
+	if (pos == NULL)
+		positions->pos[positions->last] = NULL;
+	else {
+		positions->pos[positions->last] = (cmd_pos_t *)calloc(1,
+		    sizeof (cmd_pos_t));
+		if (positions->pos[positions->last] == NULL)
+			goto nomemory;
+
+		*positions->pos[positions->last] = *pos;
+		positions->pos[positions->last]->line = strdup(pos->line);
+		if (positions->pos[positions->last]->line == NULL)
+			goto nomemory;
+	}
+
+	positions->last++;
+	return (0);
+
+nomemory:
+	print_error(NULL, gettext("Not enough memory.\n"));
+	return (-1);
+}
+
+
+
 
 /*
  * Compare two strings just like strcmp, but stop before the end of
@@ -268,12 +381,12 @@
 	    "idmap\n"
 	    "idmap -f command-file\n"
 	    "idmap show [-c] identity [targettype]\n"
-	    "idmap dump [-u|-g] [-n]\n"
-	    "idmap add -u|-g [-d] name1 name2\n"
-	    "idmap remove -u|-g -a\n"
-	    "idmap remove -u|-g name\n"
-	    "idmap remove -u|-g [-d] name1 name2\n"
-	    "idmap list [-u|-g]\n"
+	    "idmap dump [-n]\n"
+	    "idmap add [-d] name1 name2\n"
+	    "idmap remove -a\n"
+	    "idmap remove name\n"
+	    "idmap remove [-d] name1 name2\n"
+	    "idmap list\n"
 	    "idmap import [-F] [-f file] format\n"
 	    "idmap export [-f file] format\n"
 	    "idmap help\n");
@@ -282,7 +395,7 @@
 /* The handler for the "help" command. */
 static int
 /* LINTED E_FUNC_ARG_UNUSED */
-do_help(flag_t *f, int argc, char **argv)
+do_help(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
 {
 	help();
 	return (0);
@@ -295,7 +408,7 @@
 
 	stat = idmap_init(&handle);
 	if (stat < 0) {
-		(void) fprintf(stderr,
+		print_error(NULL,
 		    gettext("Connection not established (%s)\n"),
 		    idmap_stat2string(NULL, stat));
 		return (-1);
@@ -332,11 +445,15 @@
 
 	stat = idmap_udt_create(handle, &udt);
 	if (stat < 0) {
-		(void) fprintf(stderr,
+		print_error(NULL,
 		    gettext("Error initiating transaction (%s)"),
 		    idmap_stat2string(handle, stat));
 		return (-1);
 	}
+
+	if (init_positions() < 0)
+		return (-1);
+
 	return (0);
 }
 
@@ -353,43 +470,69 @@
 
 
 /* If everythings is OK, send the udt batch to idmapd  */
-static void
-fini_udt_command(int ok) {
-	idmap_stat stat;
+static int
+fini_udt_command(int ok, cmd_pos_t *pos) {
+	int rc = 0;
+	int64_t failpos;
+	idmap_stat stat, stat1;
+	cmd_pos_t *reported_pos;
 
 	if (batch_mode)
-		return;
-	if (udt == NULL)
-		return;
+		return (0);
+	if (udt == NULL) {
+		print_error(pos,
+		    gettext("Internal error: uninitiated batch.\n"));
+		return (-1);
+	}
 
 	if (ok && udt_used) {
 		stat = idmap_udt_commit(udt);
-		if (stat < 0) {
-			(void) fprintf(stderr,
-			    gettext("Error commiting transaction (%s)\n"),
-			    idmap_stat2string(handle, stat));
+		if (stat == IDMAP_SUCCESS)
+			goto out;
+
+		rc = -1;
+
+		stat1 = idmap_udt_get_error_index(udt, &failpos);
+		if (stat1 != IDMAP_SUCCESS) {
+			print_error(NULL,
+			    gettext("Error diagnosing transaction (%s)\n"),
+			    idmap_stat2string(handle, stat1));
+			goto out;
 		}
+
+
+		if (failpos < 0)
+			reported_pos = pos;
+		else
+			reported_pos = positions->pos[failpos];
+
+		print_error(reported_pos,
+		    gettext("Error commiting transaction (%s)\n"),
+		    idmap_stat2string(handle, stat));
 	}
 
+out:
 	idmap_udt_destroy(udt);
 	udt = NULL;
 	udt_used = 0;
 	fini_command();
+	fini_positions();
+	return (rc);
 }
 
 /* Convert numeric expression of the direction to it's string form */
 static char *
 direction2string(int direction) {
 	switch (direction) {
-	case DIR_BI:
+	case IDMAP_DIRECTION_BI:
 		return ("==");
-	case DIR_W2U:
+	case IDMAP_DIRECTION_W2U:
 		return ("=>");
-	case DIR_U2W:
+	case IDMAP_DIRECTION_U2W:
 		return ("<=");
 	default:
 		/* This can never happen: */
-		(void) fprintf(stderr,
+		print_error(NULL,
 		    gettext("Internal error: invalid direction.\n"));
 		return ("");
 	}
@@ -434,6 +577,22 @@
 }
 
 /*
+ * Returns 1 if c is a shell-meta-character requiring quoting even
+ * inside double quotes, 0 otherwise. It means \, " and $ .
+ *
+ * This set of characters is a subset of those in is_shell_special().
+ */
+static int
+is_dq_special(char c) {
+	if (strchr("\\\"$", c) != NULL)
+		return (1);
+	return (0);
+}
+
+
+
+
+/*
  * Quote any shell meta-characters in the given string.  If 'quote' is
  * true then use double-quotes to quote the whole string, else use
  * back-slash to quote each individual meta-character.
@@ -455,7 +614,7 @@
 	for (i = 0; i < len_orig; i++) {
 		if (is_shell_special(string[i])) {
 			noss++;
-			if (string[i] == '"' || string[i] == '\\')
+			if (is_dq_special(string[i]))
 				noqb++;
 		}
 
@@ -465,7 +624,7 @@
 	if (noss == 0) {
 		out = strdup(string);
 		if (out == NULL) {
-			(void) fprintf(stderr, gettext("Not enough memory.\n"));
+			print_error(NULL, gettext("Not enough memory.\n"));
 			return (-1);
 		}
 		*res = out;
@@ -480,7 +639,7 @@
 
 	out = (char *)malloc(len * sizeof (char));
 	if (out == NULL) {
-		(void) fprintf(stderr, gettext("Not enough memory.\n"));
+		print_error(NULL, gettext("Not enough memory.\n"));
 		return (-1);
 	}
 
@@ -490,7 +649,7 @@
 
 	for (i = 0; i < len_orig; i++) {
 		/* Quote the dangerous chars by a backslash */
-		if (quote && (string[i] == '"' || string[i] == '\\') ||
+		if (quote && is_dq_special(string[i]) ||
 			(!quote && is_shell_special(string[i]))) {
 			out[j++] = '\\';
 		}
@@ -537,12 +696,13 @@
 	return (to);
 }
 
-/* Assemble winname, e.g. "winname:bob@foo.sun.com", from name_mapping_t */
+/* Assemble winname, e.g. "winuser:bob@foo.sun.com", from name_mapping_t */
 static int
 nm2winqn(name_mapping_t *nm, char **winqn) {
 	char *out;
 	size_t length = 0;
 	int is_domain = 1;
+	char *prefix;
 
 	/* Sometimes there are no text names. Return a sid, then. */
 	if (nm->winname == NULL) {
@@ -553,7 +713,20 @@
 		return (0);
 	}
 
-	length = strlen(ID_WINNAME ":") + strlen(nm->winname);
+	switch (nm->is_user) {
+	case I_YES:
+		prefix = ID_WINUSER ":";
+		break;
+	case I_NO:
+		prefix = ID_WINGROUP ":";
+		break;
+	case I_UNKNOWN:
+		prefix = ID_WINNAME ":";
+		break;
+
+	}
+
+	length = strlen(prefix) + strlen(nm->winname);
 
 	/* Windomain is not mandatory: */
 	if (nm->windomain == NULL ||
@@ -565,12 +738,12 @@
 
 	out = (char *)malloc((length + 1) * sizeof (char));
 	if (out == NULL) {
-		(void) fprintf(stderr,
+		print_error(NULL,
 		    gettext("Not enough memory.\n"));
 		return (-1);
 	}
 
-	(void) strcpy(out, ID_WINNAME ":");
+	(void) strcpy(out, prefix);
 
 	if (!is_domain)
 		(void) strcat(out, nm->winname);
@@ -588,12 +761,13 @@
 	return (0);
 }
 
-/* Assemble a text unixname, e.g. unixname:fred */
+/* Assemble a text unixname, e.g. unixuser:fred */
 static int
 nm2unixname(name_mapping_t *nm, char **unixname) {
 	size_t length = 0;
 	char *out;
 	char *it;
+	char *prefix;
 
 	/* Sometimes there is no name, just pid: */
 	if (nm->unixname == NULL) {
@@ -607,17 +781,31 @@
 	if (shell_app(&it, nm->unixname, 0))
 		return (-1);
 
-	length = strlen(ID_UNIXNAME ":") + strlen(it);
+
+	switch (nm->is_user) {
+	case I_YES:
+		prefix = ID_UNIXUSER ":";
+		break;
+	case I_NO:
+		prefix = ID_UNIXGROUP ":";
+		break;
+	case I_UNKNOWN:
+		prefix = ID_UNIXNAME ":";
+		break;
+
+	}
+
+	length = strlen(prefix) + strlen(it);
 
 	out = (char *)malloc((length + 1) * sizeof (char));
 	if (out == NULL) {
-		(void) fprintf(stderr,
+		print_error(NULL,
 		    gettext("Not enough memory.\n"));
 		free(it);
 		return (-1);
 	}
 
-	(void) strcpy(out, ID_UNIXNAME ":");
+	(void) strcpy(out, prefix);
 	(void) strcat(out, it);
 	free(it);
 
@@ -672,7 +860,6 @@
 {
 	char *dirstring;
 	char *winname_qm, *windomain_qm, *unixname_qm;
-	char type;
 	char *winname = NULL;
 	char *winname1 = NULL;
 	char *unixname = NULL;
@@ -691,7 +878,7 @@
 	case MAPPING_ID:
 		if (pnm_format == MAPPING_ID) {
 			if (nm->sidprefix == NULL) {
-				(void) fprintf(stderr,
+				print_error(NULL,
 				    gettext("SID not given.\n"));
 				return (-1);
 			}
@@ -715,11 +902,11 @@
 		break;
 	case SMBUSERS:
 		if (!nm->is_user) {
-			(void) fprintf(stderr,
+			print_error(NULL,
 			    gettext("Group rule: "));
 			f = stderr;
-		} else 	if (nm->direction == DIR_U2W) {
-			(void) fprintf(stderr,
+		} else 	if (nm->direction == IDMAP_DIRECTION_U2W) {
+			print_error(NULL,
 			    gettext("Opposite direction of the mapping: "));
 			f = stderr;
 		}
@@ -745,7 +932,7 @@
 		break;
 	case USERMAP_CFG:
 		if (!nm->is_user) {
-			(void) fprintf(stderr,
+			print_error(NULL,
 			    gettext("Group rule: "));
 			f = stderr;
 		}
@@ -773,12 +960,10 @@
 		break;
 
 	case DEFAULT_FORMAT:
-		/* 'u', 'g' refer to -u, -g switch of idmap add */
-		type = nm->is_user ? 'u' : 'g';
 		if (nm2winqn(nm, &winname1) < 0)
 			return (-1);
 
-		if (shell_app(&winname, winname1, 0)) {
+		if (shell_app(&winname, winname1, 1)) {
 			free(winname1);
 			return (-1);
 		}
@@ -790,14 +975,14 @@
 			return (-1);
 		}
 
-		if (nm->direction == DIR_U2W) {
+		if (nm->direction == IDMAP_DIRECTION_U2W) {
 			(void) fprintf(f,
-			    "add -%c -d\t%s\t%s\n",
-			    type, unixname, winname);
+			    "add -d\t%s\t%s\n",
+			    unixname, winname);
 		} else {
 			(void) fprintf(f,
-			    "add -%c %s\t%s\t%s\n",
-			    type, nm->direction == DIR_BI ? "" : "-d",
+			    "add %s\t%s\t%s\n",
+			    nm->direction == IDMAP_DIRECTION_BI? "" : "-d",
 			    winname, unixname);
 		}
 		free(winname);
@@ -805,7 +990,7 @@
 		break;
 	default:
 		/* This can never happen: */
-		(void) fprintf(stderr,
+		print_error(NULL,
 		    gettext("Internal error: invalid print format.\n"));
 		return (-1);
 	}
@@ -818,14 +1003,14 @@
 name_mapping_init() {
 	name_mapping_t *nm = (name_mapping_t *)malloc(sizeof (name_mapping_t));
 	if (nm == NULL) {
-		(void) fprintf(stderr, gettext("Not enough memory.\n"));
+		print_error(NULL, gettext("Not enough memory.\n"));
 		return (NULL);
 	}
 	nm->winname = nm->windomain = nm->unixname = nm->sidprefix = NULL;
 	nm->rid = UNDEFINED_RID;
 	nm->is_nt4 = B_FALSE;
 	nm->is_user = I_UNKNOWN;
-	nm->direction = DIR_UNKNOWN;
+	nm->direction = IDMAP_DIRECTION_UNDEF;
 	nm->pid = UNDEFINED_UID;
 	return (nm);
 }
@@ -842,40 +1027,11 @@
 	free(nm);
 }
 
-/* Is there exactly one of -g, -u flags? */
-static int
-is_type_determined(flag_t *f)
-{
-	if (f[u_FLAG] == NULL && f[g_FLAG] == NULL || /* none */
-	    f[u_FLAG] != NULL && f[g_FLAG] != NULL) /* both */ {
-		(void) fprintf(stderr,
-		    gettext("Type (-u|-g) not determined.\n"));
-		return (0);
-	}
-	return (1);
-}
-
-/* Does user request a user-related operation? */
-static int
-is_user_wanted(flag_t *f) {
-	if (f[u_FLAG] != NULL || f[g_FLAG] == NULL)
-		return (1);
-	return (0);
-}
-
-/* Does user request a group-related operation? */
-static int
-is_group_wanted(flag_t *f) {
-	if (f[g_FLAG] != NULL || f[u_FLAG] == NULL)
-		return (1);
-	return (0);
-}
-
 
 /* dump command handler */
 static int
 /* LINTED E_FUNC_ARG_UNUSED */
-do_dump(flag_t *f, int argc, char **argv)
+do_dump(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
 {
 	idmap_stat stat;
 	idmap_iter_t *ihandle;
@@ -889,17 +1045,9 @@
 	    stdout);
 
 	for (is_user = I_YES; is_user >= I_NO; is_user--) {
-		/*
-		 * If there is exactly one of -u, -g flags, we print
-		 * only that type. Otherwise both of them:
-		 */
-		if (!is_user_wanted(f) && is_user ||
-		    !is_group_wanted(f) && !is_user)
-			continue;
-
 		stat = idmap_iter_mappings(handle, is_user, &ihandle);
 		if (stat < 0) {
-			(void) fprintf(stderr,
+			print_error(pos,
 			    gettext("Iteration handle not obtained (%s)\n"),
 			    idmap_stat2string(handle, stat));
 			rc = -1;
@@ -929,7 +1077,7 @@
 
 		/* IDMAP_ERR_NOTFOUND indicates end of the list */
 		if (stat < 0 && stat != IDMAP_ERR_NOTFOUND) {
-			(void) fprintf(stderr,
+			print_error(pos,
 			    gettext("Error during iteration (%s)\n"),
 			    idmap_stat2string(handle, stat));
 			rc = -1;
@@ -952,7 +1100,7 @@
 strndup(char *from, size_t length) {
 	char *out = (char *)malloc((length + 1) * sizeof (char));
 	if (out == NULL) {
-		(void) fprintf(stderr, gettext("Not enough memory\n"));
+		print_error(NULL, gettext("Not enough memory\n"));
 		return (NULL);
 	}
 	(void) strncpy(out, from, length);
@@ -966,7 +1114,7 @@
  * print an error message and return 0.
  */
 static int
-pid_convert(char *string, uid_t *number, int type) {
+pid_convert(char *string, uid_t *number, int type, cmd_pos_t *pos) {
 	int i;
 	long long ll;
 	char *type_string;
@@ -981,7 +1129,7 @@
 
 	for (i = 0; i < len; i++) {
 		if (!isdigit(string[i])) {
-			(void) fprintf(stderr,
+			print_error(pos,
 			    gettext("\"%s\" is not a valid %s: the non-digit"
 				" character '%c' found.\n"), string,
 			    type_string, string[i]);
@@ -994,7 +1142,7 @@
 	/* Isn't it too large? */
 	if (type == TYPE_UID && (uid_t)ll != ll ||
 	    type == TYPE_GID && (gid_t)ll != ll) {
-		(void) fprintf(stderr,
+		print_error(pos,
 		    gettext("%llu: too large for a %s.\n"), ll,
 		    type_string);
 		return (0);
@@ -1010,7 +1158,7 @@
  * message and return 0.
  */
 static int
-sid_convert(char *from, char **prefix, idmap_rid_t *rid) {
+sid_convert(char *from, char **prefix, idmap_rid_t *rid, cmd_pos_t *pos) {
 	int i, j;
 	char *cp;
 	char *ecp;
@@ -1019,14 +1167,14 @@
 	unsigned long	r;
 
 	if (strcmp_no0(from, "S-1-") != 0) {
-		(void) fprintf(stderr,
+		print_error(pos,
 		    gettext("Invalid %s \"%s\": it doesn't start "
 			"with \"%s\".\n"), ID_SID, from, "S-1-");
 		return (0);
 	}
 
 	if (strlen(from) <= strlen("S-1-")) {
-		(void) fprintf(stderr,
+		print_error(pos,
 		    gettext("Invalid %s \"%s\": the authority and RID parts are"
 			" missing.\n"),
 		    ID_SID, from);
@@ -1039,12 +1187,12 @@
 		j++, cp = strchr(cp + 1, '-')) {
 		/* can't end on a '-' */
 		if (*(cp + 1) == '\0') {
-			(void) fprintf(stderr,
+			print_error(pos,
 			    gettext("Invalid %s \"%s\": '-' at the end.\n"),
 			    ID_SID, from);
 			return (0);
 		} else 	if (*(cp + 1) == '-') {
-			(void) fprintf(stderr,
+			print_error(pos,
 			    gettext("Invalid %s \"%s\": double '-'.\n"),
 			    ID_SID, from);
 			return (0);
@@ -1055,7 +1203,7 @@
 	/* check that we only have digits and '-' */
 	i = strspn(from + 1, "0123456789-") + 1;
 	if (i < strlen(from)) {
-		(void) fprintf(stderr,
+		print_error(pos,
 		    gettext("Invalid %s \"%s\": invalid character '%c'.\n"),
 		    ID_SID, from, from[i]);
 		return (0);
@@ -1070,7 +1218,7 @@
 
 	/* errors parsing the authority or too many bits */
 	if (cp == ecp || (a == 0 && errno == EINVAL)) {
-			(void) fprintf(stderr,
+			print_error(pos,
 			gettext("Invalid %s \"%s\": unable to parse the "
 			    "authority \"%.*s\".\n"), ID_SID, from, ecp - cp,
 			cp);
@@ -1079,7 +1227,7 @@
 
 	if ((a == ULLONG_MAX && errno == ERANGE) ||
 	    (a & 0x0000ffffffffffffULL) != a) {
-		(void) fprintf(stderr,
+		print_error(pos,
 		    gettext("Invalid %s \"%s\": the authority "
 			"\"%.*s\" is too large.\n"), ID_SID, from,
 		    ecp - cp, cp);
@@ -1089,7 +1237,7 @@
 	cp = ecp;
 
 	if (j < 3) {
-		(void) fprintf(stderr,
+		print_error(pos,
 		    gettext("Invalid %s \"%s\": must have at least one RID.\n"),
 		    ID_SID, from);
 		return (0);
@@ -1098,7 +1246,7 @@
 	for (i = 2; i < j; i++) {
 		if (*cp++ != '-') {
 			/* Should never happen */
-			(void) fprintf(stderr,
+			print_error(pos,
 			    gettext("Invalid %s \"%s\": internal error:"
 				" '-' missing.\n"),
 			    ID_SID, from);
@@ -1111,7 +1259,7 @@
 		/* errors parsing the RID */
 		if (cp == ecp || (r == 0 && errno == EINVAL)) {
 			/* should never happen */
-			    (void) fprintf(stderr,
+			    print_error(pos,
 				gettext("Invalid %s \"%s\": internal error: "
 				    "unable to parse the RID "
 				    "after \"%.*s\".\n"), ID_SID,
@@ -1120,7 +1268,7 @@
 		}
 
 		if (r == ULONG_MAX && errno == ERANGE) {
-			(void) fprintf(stderr,
+			print_error(pos,
 			    gettext("Invalid %s \"%s\": the RID \"%.*s\""
 				" is too large.\n"), ID_SID,
 			    from, ecp - cp, cp);
@@ -1133,7 +1281,7 @@
 	/* check that all of the string SID has been consumed */
 	if (*cp != '\0') {
 		/* Should never happen */
-		(void) fprintf(stderr,
+		print_error(pos,
 		    gettext("Invalid %s \"%s\": internal error: "
 			"something is still left.\n"),
 		    ID_SID, from);
@@ -1145,7 +1293,7 @@
 	/* -1 for the '-' at the end: */
 	*prefix = strndup(from, prefix_end - from - 1);
 	if (*prefix == NULL) {
-		(void) fprintf(stderr,
+		print_error(pos,
 		    gettext("Not enough memory.\n"));
 		return (0);
 	}
@@ -1167,13 +1315,12 @@
  * there cannot be a protected quotation mark inside.
  */
 static char *
-ucp_qm_interior(char **line, int line_num) {
+ucp_qm_interior(char **line, cmd_pos_t *pos) {
 	char *out;
 	char *qm = strchr(*line + 1, '"');
 	if (qm == NULL) {
-		(void) fprintf(stderr,
-		    gettext("Line %d: Unclosed quotations\n"),
-		    line_num);
+		print_error(pos,
+		    gettext("Unclosed quotations\n"));
 		return (NULL);
 	}
 
@@ -1189,10 +1336,10 @@
  * reporting.
  */
 static char *
-ucp_grab_token(char **line, int line_num, const char *terminators) {
+ucp_grab_token(char **line, cmd_pos_t *pos, const char *terminators) {
 	char *token;
 	if (**line == '"')
-		token = ucp_qm_interior(line, line_num);
+		token = ucp_qm_interior(line, pos);
 	else {
 		int length = strcspn(*line, terminators);
 		token = strndup(*line, length);
@@ -1204,14 +1351,13 @@
 
 
 /*
- * Convert a line in usermap.cfg format to name_mapping. line_num is
- * the line number of input used for error reporting.
+ * Convert a line in usermap.cfg format to name_mapping.
  *
  * Return values: -1 for error, 0 for empty line, 1 for a mapping
  * found.
  */
 static int
-ucp_line2nm(char *line, int line_num, name_mapping_t *nm) {
+ucp_line2nm(char *line, cmd_pos_t *pos, name_mapping_t *nm) {
 	char *it;
 	char *token;
 	char *token2;
@@ -1226,14 +1372,13 @@
 
 	/* We do not support network qualifiers */
 	if (ucp_is_IP_qualifier(it)) {
-		(void) fprintf(stderr,
-		    gettext("Line %d: unable to handle network qualifier.\n"),
-		    line_num);
+		print_error(pos,
+		    gettext("Unable to handle network qualifier.\n"));
 		return (-1);
 	}
 
 	/* The windows name: */
-	token = ucp_grab_token(&it, line_num, " \t#\\\n@=<");
+	token = ucp_grab_token(&it, pos, " \t#\\\n@=<");
 	if (token == NULL)
 		return (-1);
 
@@ -1242,25 +1387,23 @@
 	/* Didn't we bump to the end of line? */
 	if (separator == '\0' || separator == '#') {
 		free(token);
-		(void) fprintf(stderr,
-		    gettext("Line %d: UNIX_name not found.\n"),
-		    line_num);
+		print_error(pos,
+		    gettext("UNIX_name not found.\n"));
 		return (-1);
 	}
 
 	/* Do we have a domainname? */
 	if (separator == '\\' || separator == '@') {
 		it ++;
-		token2 = ucp_grab_token(&it, line_num, " \t\n#");
+		token2 = ucp_grab_token(&it, pos, " \t\n#");
 		if (token2 == NULL) {
 			free(token);
 			return (-1);
 		} else if (*it == '\0' || *it == '#') {
 			free(token);
 			free(token2);
-			(void) fprintf(stderr,
-			    gettext("Line %d: UNIX_name not found.\n"),
-			    line_num);
+			print_error(pos,
+			    gettext("UNIX_name not found.\n"));
 		}
 
 		if (separator == '\\') {
@@ -1284,16 +1427,16 @@
 
 	/* Direction string is optional: */
 	if (strncmp(it, "==", 2) == 0) {
-		nm->direction = DIR_BI;
+		nm->direction = IDMAP_DIRECTION_BI;
 		is_direction = 1;
 	} else if (strncmp(it, "<=", 2) == 0) {
-		nm->direction = DIR_U2W;
+		nm->direction = IDMAP_DIRECTION_U2W;
 		is_direction = 1;
 	} else if (strncmp(it, "=>", 2) == 0) {
-		nm->direction = DIR_W2U;
+		nm->direction = IDMAP_DIRECTION_W2U;
 		is_direction = 1;
 	} else {
-		nm->direction = DIR_BI;
+		nm->direction = IDMAP_DIRECTION_BI;
 		is_direction = 0;
 	}
 
@@ -1302,16 +1445,15 @@
 		it += strspn(it, " \t\n");
 
 		if (*it == '\0' || *it == '#') {
-			(void) fprintf(stderr,
-			    gettext("Line %d: UNIX_name not found.\n"),
-			    line_num);
+			print_error(pos,
+			    gettext("UNIX_name not found.\n"));
 			return (-1);
 		}
 	}
 
 	/* Now unixname: */
 	it += strspn(it, " \t\n");
-	token = ucp_grab_token(&it, line_num, " \t\n#");
+	token = ucp_grab_token(&it, pos, " \t\n#");
 
 	if (token == NULL)
 		/* nm->winname to be freed by name_mapping_fini */
@@ -1319,9 +1461,8 @@
 
 	/* Neither here we support IP qualifiers */
 	if (ucp_is_IP_qualifier(token)) {
-		(void) fprintf(stderr,
-		    gettext("Line %d: unable to handle network qualifier.\n"),
-		    line_num);
+		print_error(pos,
+		    gettext("Unable to handle network qualifier.\n"));
 		free(token);
 		return (-1);
 	}
@@ -1332,9 +1473,8 @@
 
 	/* Does something remain on the line */
 	if (*it  != '\0' && *it != '#') {
-		(void) fprintf(stderr,
-		    gettext("Line %d: unrecognized parameters \"%s\".\n"),
-		    line_num, it);
+		print_error(pos,
+		    gettext("Unrecognized parameters \"%s\".\n"), it);
 		return (-1);
 	}
 
@@ -1351,7 +1491,7 @@
  *    rc = 1: mapping found and there remains other on the line
  */
 static int
-sup_line2nm(char *line, int line_num, name_mapping_t *nm) {
+sup_line2nm(char *line, cmd_pos_t *pos, name_mapping_t *nm) {
 	static char *ll = NULL;
 	static char *unixname = NULL;
 	static size_t unixname_l = 0;
@@ -1377,12 +1517,12 @@
 	if (*ll == '\0'|| *ll == '#')
 		return (0);
 
-	token = ucp_grab_token(&ll, line_num, " \t\n");
+	token = ucp_grab_token(&ll, pos, " \t\n");
 	if (token == NULL)
 		return (-1);
 
 	nm->is_nt4 = 0;
-	nm->direction = DIR_W2U;
+	nm->direction = IDMAP_DIRECTION_W2U;
 
 	nm->windomain = NULL;
 	nm->winname = token;
@@ -1396,18 +1536,18 @@
 
 /* Parse line to name_mapping_t. Basicaly just a format switch. */
 static int
-line2nm(char *line, int line_num, name_mapping_t *nm, format_t f) {
+line2nm(char *line, cmd_pos_t *pos, name_mapping_t *nm, format_t f) {
 	switch (f) {
 	case USERMAP_CFG:
 		if (line == NULL)
 			return (0);
 		else
-			return (ucp_line2nm(line, line_num, nm));
+			return (ucp_line2nm(line, pos, nm));
 	case SMBUSERS:
-		return (sup_line2nm(line, line_num, nm));
+		return (sup_line2nm(line, pos, nm));
 	default:
 		/* This can never happen */
-		(void) fprintf(stderr,
+		print_error(pos,
 		    gettext("Internal error: invalid line format.\n"));
 	}
 
@@ -1420,7 +1560,7 @@
 ff2format(char *ff, int is_mandatory) {
 
 	if (ff == NULL && is_mandatory) {
-		(void) fprintf(stderr, gettext("Format not given.\n"));
+		print_error(NULL, gettext("Format not given.\n"));
 		return (UNDEFINED_FORMAT);
 	}
 
@@ -1433,7 +1573,7 @@
 	if (strcasecmp(ff, "smbusers") == 0)
 		return (SMBUSERS);
 
-	(void) fprintf(stderr,
+	print_error(NULL,
 		    gettext("The only known formats are: \"usermap.cfg\" and "
 			"\"smbusers\".\n"));
 	return (UNDEFINED_FORMAT);
@@ -1441,36 +1581,40 @@
 
 /* Delete all namerules of the given type */
 static int
-flush_nm(boolean_t is_user)
+flush_nm(boolean_t is_user, cmd_pos_t *pos)
 {
 	idmap_stat stat;
 
 	stat = idmap_udt_flush_namerules(udt, is_user);
 	if (stat < 0) {
-		(void) fprintf(stderr,
+		print_error(pos,
 		    is_user ? gettext("Unable to flush users (%s).\n")
 		    : gettext("Unable to flush groups (%s).\n"),
 		    idmap_stat2string(handle, stat));
 		return (-1);
 	}
+
+	if (positions_add(pos) < 0)
+		return (-1);
+
 	return (0);
 }
 
 /* import command handler */
 static int
 /* LINTED E_FUNC_ARG_UNUSED */
-do_import(flag_t *f, int argc, char **argv)
+do_import(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
 {
 	name_mapping_t *nm;
+	cmd_pos_t pos2;
 	char line[MAX_INPUT_LINE_SZ];
 	format_t format;
 	int rc = 0;
 	idmap_stat stat;
-	int line_num;
 	FILE *file = NULL;
 
 	if (batch_mode) {
-		(void) fprintf(stderr,
+		print_error(pos,
 		    gettext("Import is not allowed in the batch mode.\n"));
 		return (-1);
 	}
@@ -1484,15 +1628,13 @@
 
 	/* We don't flush groups in the usermap.cfg nor smbusers format */
 	if (f[F_FLAG] != NULL &&
-	    flush_nm(B_TRUE) < 0 &&
+	    flush_nm(B_TRUE, pos) < 0 &&
 	    (format == USERMAP_CFG || format == SMBUSERS ||
-	    flush_nm(B_FALSE) < 0)) {
+	    flush_nm(B_FALSE, pos) < 0)) {
 		rc = -1;
 		goto cleanup;
 	}
 
-	line_num = 0;
-
 	/* Where we import from? */
 	if (f[f_FLAG] == NULL)
 		file = stdin;
@@ -1504,10 +1646,12 @@
 		}
 	}
 
+	pos2.linenum = 0;
+	pos2.line = line;
 
 	while (fgets(line, MAX_INPUT_LINE_SZ, file)) {
 		char *line2 = line;
-		line_num++;
+		pos2.linenum++;
 
 		/*
 		 * In SMBUSERS format there can be more mappings on
@@ -1520,7 +1664,7 @@
 				goto cleanup;
 			}
 
-			rc = line2nm(line2, line_num, nm, format);
+			rc = line2nm(line2, &pos2, nm, format);
 			line2 = NULL;
 
 			if (rc < 1) {
@@ -1532,25 +1676,29 @@
 			    nm->is_user ? B_TRUE : B_FALSE, nm->winname,
 			    nm->unixname, nm->is_nt4, nm->direction);
 			if (stat < 0) {
-				(void) fprintf(stderr,
+				print_error(&pos2,
 				    gettext("Transaction error (%s)\n"),
 				    idmap_stat2string(handle, stat));
 				rc = -1;
 			}
 
+			if (rc >= 0)
+				rc = positions_add(&pos2);
+
 			name_mapping_fini(nm);
 
 		} while (rc >= 0);
 
 		if (rc < 0) {
-			(void) fprintf(stderr,
+			print_error(NULL,
 			    gettext("Import canceled.\n"));
 			break;
 		}
 	}
 
 cleanup:
-	fini_udt_command(rc < 0 ? 0 : 1);
+	if (fini_udt_command((rc < 0 ? 0 : 1), pos))
+		rc = -1;
 	if (file != NULL && file != stdin)
 		(void) fclose(file);
 	return (rc);
@@ -1563,7 +1711,7 @@
  * file fi.
  */
 static int
-list_name_mappings(int list_users, int list_groups, format_t format, FILE *fi)
+list_name_mappings(format_t format, FILE *fi)
 {
 	idmap_stat stat;
 	idmap_iter_t *ihandle;
@@ -1571,10 +1719,6 @@
 	int is_user;
 
 	for (is_user = I_YES; is_user >= I_NO; is_user--) {
-		if (is_user && !list_users)
-			continue;
-		if (!is_user && !list_groups)
-			continue;
 		/* Only users can be in USERMAP_CFG format, not a group */
 		if (!is_user && format == USERMAP_CFG)
 			continue;
@@ -1582,7 +1726,7 @@
 		stat = idmap_iter_namerules(handle, NULL, is_user, NULL,
 		    NULL, &ihandle);
 		if (stat < 0) {
-			(void) fprintf(stderr,
+			print_error(NULL,
 			    gettext("Iteration handle not obtained (%s)\n"),
 			    idmap_stat2string(handle, stat));
 			idmap_iter_destroy(ihandle);
@@ -1613,7 +1757,7 @@
 		(void) print_mapping_fini();
 
 		if (stat < 0 && stat !=  IDMAP_ERR_NOTFOUND) {
-			(void) fprintf(stderr,
+			print_error(NULL,
 			    gettext("Error during iteration (%s)\n"),
 			    idmap_stat2string(handle, stat));
 			idmap_iter_destroy(ihandle);
@@ -1628,7 +1772,7 @@
 /* Export command handler  */
 static int
 /* LINTED E_FUNC_ARG_UNUSED */
-do_export(flag_t *f, int argc, char **argv) {
+do_export(flag_t *f, int argc, char **argv, cmd_pos_t *pos) {
 	int rc;
 	format_t format;
 	FILE *fi;
@@ -1654,10 +1798,7 @@
 	}
 
 	/* List the requested types: */
-	rc = list_name_mappings(is_user_wanted(f),
-	    is_group_wanted(f),
-	    format,
-	    fi);
+	rc = list_name_mappings(format, fi);
 
 	fini_command();
 
@@ -1670,7 +1811,7 @@
 /* List command handler */
 static int
 /* LINTED E_FUNC_ARG_UNUSED */
-do_list_name_mappings(flag_t *f, int argc, char **argv)
+do_list_name_mappings(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
 {
 	int rc;
 
@@ -1679,10 +1820,7 @@
 	}
 
 	/* List the requested types: */
-	rc = list_name_mappings(is_user_wanted(f),
-	    is_group_wanted(f),
-	    DEFAULT_FORMAT,
-	    stdout);
+	rc = list_name_mappings(DEFAULT_FORMAT, stdout);
 
 	fini_command();
 	return (rc);
@@ -1713,7 +1851,7 @@
  *                1  ... determined
  */
 static int
-name2parts(char *name, name_mapping_t *nm) {
+name2parts(char *name, name_mapping_t *nm, cmd_pos_t *pos) {
 	char *it;
 	int is_win = I_NO;
 	int is_unix = I_NO;
@@ -1723,17 +1861,70 @@
 
 	/* If it starts with type string, that is easy: */
 	if (it = strchr(name, ':')) {
-		if (strcmp_no0(name, ID_UNIXNAME ":") == 0) {
+		if (strcmp_no0(name, ID_UNIXUSER ":") == 0) {
+			if (nm->is_user == I_NO) {
+				print_error(pos,
+				    gettext("%s - invalid combination of user"
+					" and group identity types.\n"),
+				    ID_UNIXUSER);
+				return (-1);
+			}
+			if (nm->unixname != NULL)
+				return (0);
+
+			nm->is_user = I_YES;
+			is_unix = I_YES;
+
+		} else if (strcmp_no0(name, ID_UNIXGROUP ":") == 0) {
+			if (nm->is_user == I_YES) {
+				print_error(pos,
+				    gettext("%s - invalid combination of user"
+					" and group identity types.\n"),
+				    ID_UNIXGROUP);
+				return (-1);
+			}
 			if (nm->unixname != NULL)
 				return (0);
+
+			nm->is_user = I_NO;
 			is_unix = I_YES;
+
+		} else if (strcmp_no0(name, ID_WINUSER ":") == 0) {
+			if (nm->is_user == I_NO) {
+				print_error(pos,
+				    gettext("%s - invalid combination of user"
+					" and group identity types.\n"),
+				    ID_WINUSER);
+				return (-1);
+			}
+			if (nm->winname != NULL)
+				return (0);
+
+			nm->is_user = I_YES;
+			is_win = I_YES;
+
+		} else if (strcmp_no0(name, ID_WINGROUP ":") == 0) {
+			if (nm->is_user == I_YES) {
+				print_error(pos,
+				    gettext("%s - invalid combination of user"
+					" and group identity types.\n"),
+				    ID_WINGROUP);
+				return (-1);
+			}
+			if (nm->winname != NULL)
+				return (0);
+
+			nm->is_user = I_NO;
+			is_win = I_YES;
+
 		} else if (strcmp_no0(name, ID_WINNAME ":") == 0) {
 			if (nm->winname != NULL)
 				return (0);
 			is_win = I_YES;
 		} else {
-			(void) fprintf(stderr,
-			    gettext("Error: invalid identity type\n"));
+			print_error(pos,
+			    gettext("Error: invalid identity type \"%.*s\"\n"),
+			    it - name, name);
 			return (-1);
 		}
 		name = it + 1;
@@ -1742,14 +1933,14 @@
 	/* If it contains '@' or '\\', then it is a winname with domain */
 	if (!is_unix && nm->winname == NULL) {
 		if ((it = strchr(name, '@')) != NULL) {
-			int length = it-name+1;
+			int length = it - name + 1;
 			nm->winname = (char *)malloc(length * sizeof (char));
 			(void) strncpy(nm->winname, name, length - 1);
 			nm->winname[length - 1] = '\0';
 			nm->windomain = strdup(it + 1);
 			return (1);
 		} else if ((it = strrchr(name, '\\')) != NULL) {
-			int length = it-name+1;
+			int length = it - name + 1;
 			nm->windomain = (char *)malloc(length * sizeof (char));
 			(void) strncpy(nm->windomain, name, length - 1);
 			nm->windomain[length - 1] = '\0';
@@ -1784,7 +1975,7 @@
 
 /* add command handler. */
 static int
-do_add_name_mapping(flag_t *f, int argc, char **argv)
+do_add_name_mapping(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
 {
 	name_mapping_t *nm;
 	int rc = 0;
@@ -1793,15 +1984,14 @@
 	idmap_stat stat;
 
 
-	/* Two arguments and exactly one of -u, -g must be specified */
+	/* Exactly two arguments must be specified */
 	if (argc < 2) {
-		(void) fprintf(stderr, gettext("Not enough arguments.\n"));
+		print_error(pos, gettext("Not enough arguments.\n"));
 		return (-1);
 	} else if (argc > 2)  {
-		(void) fprintf(stderr, gettext("Too many arguments.\n"));
+		print_error(pos, gettext("Too many arguments.\n"));
 		return (-1);
-	} else if (!is_type_determined(f))
-		return (-1);
+	}
 
 	/*
 	 * Direction can be determined by the opposite name, so we
@@ -1812,10 +2002,8 @@
 	if (nm == NULL)
 		return (-1);
 
-	nm->is_user = f[u_FLAG] != NULL ? I_YES : I_NO;
-
 	for (i = 0; i < 3; i++) {
-		switch (name2parts(argv[i % 2], nm)) {
+		switch (name2parts(argv[i % 2], nm, pos)) {
 		case -1:
 			name_mapping_fini(nm);
 			return (-1);
@@ -1828,15 +2016,17 @@
 	}
 
 	if (nm->winname == NULL || nm->unixname == NULL) {
-		(void) fprintf(stderr, gettext("Name types not determined.\n"));
+		print_error(pos, gettext("Name types not determined.\n"));
 		name_mapping_fini(nm);
 		return (-1);
 	}
 
 	if (f[d_FLAG] != NULL)
-		nm->direction = is_argv0_unix ? DIR_U2W : DIR_W2U;
+		nm->direction = is_argv0_unix
+		    ? IDMAP_DIRECTION_U2W
+		    : IDMAP_DIRECTION_W2U;
 	else
-		nm->direction = DIR_BI;
+		nm->direction = IDMAP_DIRECTION_BI;
 
 	/* Now let us write it: */
 
@@ -1855,55 +2045,59 @@
 	(void) print_mapping_fini();
 
 	if (stat < 0) {
-		(void) fprintf(stderr,
+		print_error(pos,
 		    gettext("Mapping not created (%s)\n"),
 		    idmap_stat2string(handle, stat));
 		rc = -1;
 	}
 
+	if (rc == 0)
+		rc = positions_add(pos);
+
 cleanup:
 	name_mapping_fini(nm);
-	fini_udt_command(1);
+	if (fini_udt_command(1, pos))
+		rc = -1;
 	return (rc);
 }
 
 /* remove command handler */
 static int
-do_remove_name_mapping(flag_t *f, int argc, char **argv)
+do_remove_name_mapping(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
 {
 	name_mapping_t *nm;
 	int rc = 0;
 	int i;
 	int is_argv0_unix = -1;
 	idmap_stat stat;
+	int is_user;
 
 	/* "-a" means we flush all of them */
 	if (f[a_FLAG] != NULL) {
 		if (argc) {
-			(void) fprintf(stderr,
+			print_error(pos,
 			    gettext("Too many arguments.\n"));
 			return (-1);
 		}
 
-		if (!is_type_determined(f))
-			return (-1);
-
 		if (init_udt_command())
 			return (-1);
-		rc = flush_nm(f[u_FLAG] != NULL ? B_TRUE : B_FALSE);
-
-		fini_udt_command(rc ? 0 : 1);
+		rc = flush_nm(B_TRUE, pos);
+
+		if (rc >= 0)
+			rc = flush_nm(B_FALSE, pos);
+
+		if (fini_udt_command(rc ? 0 : 1, pos))
+			rc = -1;
 		return (rc);
 	}
 
 	/* Contrary to add_name_mapping, we can have only one argument */
 	if (argc < 1) {
-		(void) fprintf(stderr, gettext("Not enough arguments.\n"));
+		print_error(pos, gettext("Not enough arguments.\n"));
 		return (-1);
 	} else if (argc > 2) {
-		(void) fprintf(stderr, gettext("Too many arguments.\n"));
-		return (-1);
-	} else if (!is_type_determined(f)) {
+		print_error(pos, gettext("Too many arguments.\n"));
 		return (-1);
 	} else if (
 		/* both -f and -t: */
@@ -1912,7 +2106,7 @@
 	    argc == 1 && f[d_FLAG] != NULL ||
 		/* -f or -t with two arguments: */
 	    argc == 2 && (f[f_FLAG] != NULL || f[t_FLAG] != NULL)) {
-		(void) fprintf(stderr,
+		print_error(pos,
 		    gettext("Direction ambiguous.\n"));
 		return (-1);
 	}
@@ -1926,10 +2120,8 @@
 	if (nm == NULL)
 		return (-1);
 
-	nm->is_user = f[u_FLAG] != NULL ? I_YES : I_NO;
-
 	for (i = 0; i < 2 * argc - 1; i++) {
-		switch (name2parts(argv[i % 2], nm)) {
+		switch (name2parts(argv[i % 2], nm, pos)) {
 		case -1:
 			name_mapping_fini(nm);
 			return (-1);
@@ -1942,42 +2134,57 @@
 
 
 	if (nm->winname == NULL && nm->unixname == NULL) {
-		(void) fprintf(stderr, gettext("Name types not determined.\n"));
+		print_error(pos, gettext("Name types not determined.\n"));
 		name_mapping_fini(nm);
 		return (-1);
 	}
 
 	/*
 	 * If the direction is not specified by a -d/-f/-t flag, then it
-	 * is DIR_UNKNOWN, because in that case we want to remove any
-	 * mapping. If it was DIR_BI, idmap_api would delete a
-	 * bidirectional one only.
+	 * is IDMAP_DIRECTION_UNDEF, because in that case we want to
+	 * remove any mapping. If it was IDMAP_DIRECTION_BI, idmap_api would
+	 * delete a bidirectional one only.
 	 */
 	if (f[d_FLAG] != NULL || f[f_FLAG] != NULL)
-		nm->direction = is_argv0_unix ? DIR_U2W : DIR_W2U;
+		nm->direction = is_argv0_unix
+		    ? IDMAP_DIRECTION_U2W
+		    : IDMAP_DIRECTION_W2U;
 	else if (f[t_FLAG] != NULL)
-		nm->direction = is_argv0_unix ? DIR_W2U : DIR_U2W;
+		nm->direction = is_argv0_unix
+		    ? IDMAP_DIRECTION_W2U
+		    : IDMAP_DIRECTION_U2W;
 	else
-		nm->direction = DIR_UNKNOWN;
+		nm->direction = IDMAP_DIRECTION_UNDEF;
 
 	if (init_udt_command()) {
 		name_mapping_fini(nm);
 		return (-1);
 	}
 
-	stat = idmap_udt_rm_namerule(udt, nm->is_user ? B_TRUE : B_FALSE,
-	    nm->windomain, nm->winname, nm->unixname, nm->direction);
-
-	if (stat < 0) {
-		(void) fprintf(stderr,
-		    gettext("Mapping not deleted (%s)\n"),
-		    idmap_stat2string(handle, stat));
-		rc = -1;
+	for (is_user = I_YES; is_user >= I_NO; is_user--) {
+		if ((is_user == I_YES && nm->is_user == I_NO) ||
+		    (is_user == I_NO && nm->is_user == I_YES))
+			continue;
+
+		stat = idmap_udt_rm_namerule(udt, is_user ? B_TRUE : B_FALSE,
+		    nm->windomain, nm->winname, nm->unixname, nm->direction);
+
+		if (stat < 0) {
+			print_error(pos,
+			    gettext("Mapping not deleted (%s)\n"),
+			    idmap_stat2string(handle, stat));
+			rc = -1;
+			break;
+		}
 	}
 
+	if (rc == 0)
+		rc = positions_add(pos);
+
 cleanup:
 	name_mapping_fini(nm);
-	fini_udt_command(1);
+	if (fini_udt_command(1, pos))
+		rc = -1;
 	return (rc);
 }
 
@@ -1985,7 +2192,7 @@
 /* exit command handler */
 static int
 /* LINTED E_FUNC_ARG_UNUSED */
-do_exit(flag_t *f, int argc, char **argv) {
+do_exit(flag_t *f, int argc, char **argv, cmd_pos_t *pos) {
 	return (0);
 }
 
@@ -1993,7 +2200,7 @@
 /* debug command handler: just print the parameters */
 static int
 /* LINTED E_STATIC_UNUSED */
-debug_print_params(flag_t *f, int argc, char **argv)
+debug_print_params(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
 {
 	int i;
 #if 0
@@ -2034,6 +2241,8 @@
 		*to = sid_format(nm->sidprefix, nm->rid);
 		return (0);
 	case TYPE_WN:
+	case TYPE_WU:
+	case TYPE_WG:
 		return (nm2winqn(nm, to));
 	case TYPE_UID:
 	case TYPE_GID:
@@ -2044,10 +2253,12 @@
 		else
 			return (0);
 	case TYPE_UN:
+	case TYPE_UU:
+	case TYPE_UG:
 		return (nm2unixname(nm, to));
 	default:
 		/* This can never happen: */
-		(void) fprintf(stderr,
+		print_error(NULL,
 		    gettext("Internal error: invalid name type.\n"));
 		return (-1);
 	}
@@ -2056,7 +2267,7 @@
 
 /* show command handler */
 static int
-do_show_mapping(flag_t *f, int argc, char **argv)
+do_show_mapping(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
 {
 	idmap_stat stat = 0;
 	int flag;
@@ -2069,11 +2280,11 @@
 	char *toname;
 
 	if (argc == 0) {
-		(void) fprintf(stderr,
+		print_error(pos,
 		    gettext("No identity given\n"));
 		return (-1);
 	} else if (argc > 2) {
-		(void) fprintf(stderr,
+		print_error(pos,
 		    gettext("Too many arguments.\n"));
 		return (-1);
 	}
@@ -2094,17 +2305,39 @@
 		type_from = TYPE_GID;
 	else if ((root = get_root(argv[0], ID_SID ":")) != NULL)
 		type_from = TYPE_SID;
-	else if (name2parts(argv[0], nm) > 0) {
-		if (nm->unixname != NULL)
-			type_from = TYPE_UN;
-		else
-			type_from = TYPE_WN;
-	} else {
-		(void) fprintf(stderr,
-		    gettext("Invalid type.\n"));
-		stat = IDMAP_ERR_ARG;
-		goto cleanup;
-	}
+	else
+		switch (name2parts(argv[0], nm, pos)) {
+		case 1:		/* name2parts determined the type */
+			if (nm->unixname != NULL)
+				switch (nm->is_user) {
+				case I_YES:
+					type_from = TYPE_UU;
+					break;
+				case I_NO:
+					type_from = TYPE_UG;
+					break;
+				default:
+					type_from = TYPE_UN;
+				}
+			else
+				switch (nm->is_user) {
+				case I_YES:
+					type_from = TYPE_WU;
+					break;
+				case I_NO:
+					type_from = TYPE_WG;
+					break;
+				default:
+					type_from = TYPE_WN;
+				}
+			break;
+		case 0:		/* name2parts didn't determine the type: */
+			print_error(pos, gettext("Invalid type.\n"));
+			/* LINTED E_CASE_FALLTHRU */
+		case -1:
+			stat = IDMAP_ERR_ARG;
+			goto cleanup;
+		}
 
 	/* Second, determine type_to: */
 	if (argc < 2) {
@@ -2117,32 +2350,51 @@
 		type_to = TYPE_GID;
 	else if (strcasecmp(argv[1], ID_SID) == 0)
 		type_to = TYPE_SID;
-	else if (strcmp(argv[1], ID_UNIXNAME) == 0)
-		type_to = TYPE_UN;
+	else if (strcmp(argv[1], ID_UNIXUSER) == 0)
+		type_to = TYPE_UU;
+	else if (strcmp(argv[1], ID_UNIXGROUP) == 0)
+		type_to = TYPE_UG;
 	else if (strcmp(argv[1], ID_WINNAME) == 0)
 		type_to = TYPE_WN;
+	else if (strcmp(argv[1], ID_WINUSER) == 0)
+		type_to = TYPE_WU;
+	else if (strcmp(argv[1], ID_WINGROUP) == 0)
+		type_to = TYPE_WG;
 	else {
-		(void) fprintf(stderr,
-		    gettext("Ivnalid target type.\n"));
+		print_error(pos,
+		    gettext("Invalid target type.\n"));
 		stat = IDMAP_ERR_ARG;
 		goto cleanup;
 	}
 
 	/* Are both arguments the same OS side? */
 	if (!(type_from & IS_WIN ^ type_to & IS_WIN)) {
-		(void) fprintf(stderr,
+		print_error(pos,
 		    gettext("Direction ambiguous.\n"));
 		stat = IDMAP_ERR_ARG;
 		goto cleanup;
 	}
 
+	/* Are both arguments the same userness/groupness? */
+	if ((type_from & IS_USER) && (type_to & IS_GROUP)) {
+		print_error(pos,
+		    gettext("Mapping of user to group impossible.\n"));
+		stat = IDMAP_ERR_ARG;
+		goto cleanup;
+	} else if ((type_from & IS_GROUP) && (type_to & IS_USER)) {
+		print_error(pos,
+		    gettext("Mapping of group to user impossible.\n"));
+		stat = IDMAP_ERR_ARG;
+		goto cleanup;
+	}
+
 	if (type_from == TYPE_SID) {
-		if (!sid_convert(root, &nm->sidprefix, &nm->rid)) {
+		if (!sid_convert(root, &nm->sidprefix, &nm->rid, pos)) {
 			stat = IDMAP_ERR_ARG;
 			goto cleanup;
 		}
 	} else if (type_from == TYPE_UID || type_from == TYPE_GID) {
-		if (!pid_convert(root, &nm->pid, type_from)) {
+		if (!pid_convert(root, &nm->pid, type_from, pos)) {
 			stat = IDMAP_ERR_ARG;
 			goto cleanup;
 		}
@@ -2202,7 +2454,7 @@
 		/* Create an in-memory structure for all the batch: */
 		stat = idmap_get_create(handle, &ghandle);
 		if (stat < 0) {
-			(void) fprintf(stderr,
+			print_error(pos,
 			    gettext("Unable to create handle for communicating"
 			    " with idmapd(1M) (%s)\n"),
 			    idmap_stat2string(handle, stat));
@@ -2253,13 +2505,13 @@
 			nm->is_user = I_NO;
 		} else {
 			/* This can never happen: */
-			(void) fprintf(stderr,
+			print_error(pos,
 			    gettext("Internal error in show.\n"));
 			exit(1);
 		}
 
 		if (stat < 0) {
-			(void) fprintf(stderr,
+			print_error(pos,
 			    gettext("Request for %.3s not sent (%s)\n"),
 			    argv[0], idmap_stat2string(handle, stat));
 			idmap_get_destroy(ghandle);
@@ -2269,7 +2521,7 @@
 		/* Send the batch to idmapd and obtain results: */
 		stat = idmap_get_mappings(ghandle);
 		if (stat < 0) {
-			(void) fprintf(stderr,
+			print_error(pos,
 			    gettext("Mappings not obtained because of"
 			    " RPC problem (%s)\n"),
 			    idmap_stat2string(handle, stat));
@@ -2292,7 +2544,7 @@
 	 * the case of error:
 	 */
 	if (map_stat < 0) {
-		(void) fprintf(stderr,
+		print_error(pos,
 		    gettext("%s\n"),
 		    idmap_stat2string(handle, map_stat));
 		if (flag == IDMAP_REQ_FLG_NO_NEW_ID_ALLOC)
@@ -2300,6 +2552,19 @@
 	}
 
 
+	/*
+	 * idmapd returns fallback uid/gid in case of errors. However
+	 * it uses special sentinel value i.e 4294967295 (or -1) to
+	 * indicate that falbback pid is not available either. In such
+	 * case idmap(1M) should not display the mapping because there
+	 * is no fallback mapping.
+	 */
+
+	if (type_to == TYPE_UID && nm->pid == UNDEFINED_UID ||
+	    type_to == TYPE_GID && nm->pid == (uid_t)UNDEFINED_GID) {
+		goto cleanup;
+	}
+
 	if (nm2type(nm, type_from, &fromname) < 0)
 		goto cleanup;
 
@@ -2357,7 +2622,8 @@
 
 	if (batch_mode) {
 		batch_mode = 0;
-		fini_udt_command(rc == 0 ? 1 : 0);
+		if (fini_udt_command(rc == 0 ? 1 : 0, NULL))
+			rc = -1;
 	}
 
 	(void) engine_fini();
--- a/usr/src/cmd/idmap/idmap/idmap_engine.c	Mon Sep 17 08:01:53 2007 -0700
+++ b/usr/src/cmd/idmap/idmap/idmap_engine.c	Mon Sep 17 08:07:28 2007 -0700
@@ -112,7 +112,7 @@
 
 /* determine which subcommand is argv[0] and execute its handler */
 static int
-run_command(int argc, char **argv) {
+run_command(int argc, char **argv, cmd_pos_t *pos) {
 	int i;
 
 	if (argc == 0) {
@@ -134,7 +134,10 @@
 			return (-1);
 		}
 
-		rc = my_comv[i].p_do_func(flags, argc - optind, argv + optind);
+		rc = my_comv[i].p_do_func(flags,
+		    argc - optind,
+		    argv + optind,
+		    pos);
 
 		return (rc);
 	}
@@ -402,7 +405,7 @@
 			}
 		}
 
-		rc = run_command(my_argc, my_argv);
+		rc = run_command(my_argc, my_argv, NULL);
 
 		if (strcmp(my_argv[0], "exit") == 0 && rc == 0) {
 			break;
@@ -425,6 +428,7 @@
 	int is_stdin;
 	int rc = -1;
 	char line[MAX_CMD_LINE_SZ];
+	cmd_pos_t pos;
 
 	if (name == NULL || strcmp("-", name) == 0) {
 		f = stdin;
@@ -438,7 +442,11 @@
 		}
 	}
 
+	pos.linenum = 0;
+	pos.line = line;
+
 	while (fgets(line, MAX_CMD_LINE_SZ, f)) {
+		pos.linenum ++;
 
 		if (line2array(line) < 0) {
 			(void) fprintf(stderr,
@@ -460,7 +468,7 @@
 			break;
 		}
 
-		rc = run_command(my_argc, my_argv);
+		rc = run_command(my_argc, my_argv, &pos);
 		my_argv_clean();
 	}
 
@@ -569,7 +577,7 @@
 		goto cleanup;
 	}
 
-	rc = run_command(argc, argv);
+	rc = run_command(argc, argv, NULL);
 
 cleanup:
 	return (rc);
--- a/usr/src/cmd/idmap/idmap/idmap_engine.h	Mon Sep 17 08:01:53 2007 -0700
+++ b/usr/src/cmd/idmap/idmap/idmap_engine.h	Mon Sep 17 08:07:28 2007 -0700
@@ -48,13 +48,21 @@
 #define	IDMAP_ENG_ERROR -1
 #define	IDMAP_ENG_ERROR_SILENT -2
 
+typedef struct cmd_pos {
+	int linenum;		/* line number */
+	char *line;		/* line content */
+} cmd_pos_t;
+
+
 typedef struct cmd_ops {
 	const char *cmd;	/* the subcommand */
 	const char *options;	/* getopt string for the subcommand params */
-	int (*p_do_func)(flag_t *f, int argc, char **argv); /* handle */
+	int (*p_do_func)(flag_t *f,
+	    int argc,
+	    char **argv,
+	    cmd_pos_t *pos); /* handle */
 } cmd_ops_t;
 
-
 extern int engine_init(int comc, cmd_ops_t *comv, int argc, char **argv,
     int *is_batch_mode);
 extern int engine_fini();
--- a/usr/src/cmd/idmap/idmapd/dbutils.c	Mon Sep 17 08:01:53 2007 -0700
+++ b/usr/src/cmd/idmap/idmapd/dbutils.c	Mon Sep 17 08:07:28 2007 -0700
@@ -56,8 +56,6 @@
 
 #define	EMPTY_NAME(name)	(*name == 0 || strcmp(name, "\"\"") == 0)
 
-#define	EMPTY_STRING(str)	(str == NULL || *str == 0)
-
 #define	DO_NOT_ALLOC_NEW_ID_MAPPING(req)\
 		(req->flag & IDMAP_REQ_FLG_NO_NEW_ID_ALLOC)
 
@@ -478,29 +476,21 @@
  */
 idmap_retcode
 gen_sql_expr_from_utf8str(const char *prefix, const char *col,
-		const char *op, idmap_utf8str *value,
+		const char *op, char *value,
 		const char *suffix, char **out) {
-	char		*str;
-	idmap_stat	retcode;
-
 	if (out == NULL)
 		return (IDMAP_ERR_ARG);
 
 	if (value == NULL)
 		return (IDMAP_SUCCESS);
 
-	retcode = idmap_utf82str(&str, 0, value);
-	if (retcode != IDMAP_SUCCESS)
-		return (retcode);
-
 	if (prefix == NULL)
 		prefix = "";
 	if (suffix == NULL)
 		suffix = "";
 
 	*out = sqlite_mprintf("%s %s %s %Q %s",
-			prefix, col, op, str, suffix);
-	idmap_free(str);
+			prefix, col, op, value, suffix);
 	if (*out == NULL)
 		return (IDMAP_ERR_MEMORY);
 	return (IDMAP_SUCCESS);
@@ -685,23 +675,12 @@
 add_namerule(sqlite *db, idmap_namerule *rule) {
 	char		*sql = NULL;
 	idmap_stat	retcode;
-	char		*windomain = NULL, *winname = NULL, *dom = NULL;
-	char		*unixname = NULL;
+	char		*dom = NULL;
 	int		w2u_order, u2w_order;
 	char		w2ubuf[11], u2wbuf[11];
 
-	retcode = idmap_utf82str(&windomain, 0, &rule->windomain);
-	if (retcode != IDMAP_SUCCESS)
-		goto out;
-	retcode = idmap_utf82str(&winname, 0, &rule->winname);
-	if (retcode != IDMAP_SUCCESS)
-		goto out;
-	retcode = idmap_utf82str(&unixname, 0, &rule->unixname);
-	if (retcode != IDMAP_SUCCESS)
-		goto out;
-
-	retcode = get_namerule_order(winname, windomain, unixname,
-			rule->direction, &w2u_order, &u2w_order);
+	retcode = get_namerule_order(rule->winname, rule->windomain,
+	    rule->unixname, rule->direction, &w2u_order, &u2w_order);
 	if (retcode != IDMAP_SUCCESS)
 		goto out;
 
@@ -716,9 +695,9 @@
 	 * 2) Use "" instead of NULL for "no domain"
 	 */
 
-	if (windomain != NULL)
-		dom = windomain;
-	else if (lookup_wksids_name2sid(winname, NULL, NULL, NULL)
+	if (rule->windomain != NULL)
+		dom = rule->windomain;
+	else if (lookup_wksids_name2sid(rule->winname, NULL, NULL, NULL)
 	    == IDMAP_SUCCESS) {
 		/* well-known SIDs don't need domain */
 		dom = "";
@@ -737,8 +716,8 @@
 		"VALUES(%d, %Q, %Q, %d, %Q, %q, %q);",
 		rule->is_user?1:0,
 		dom,
-		winname, rule->is_nt4?1:0,
-		unixname,
+		rule->winname, rule->is_nt4?1:0,
+		rule->unixname,
 		w2u_order?w2ubuf:NULL,
 		u2w_order?u2wbuf:NULL);
 	UNLOCK_CONFIG();
@@ -755,12 +734,6 @@
 		retcode = IDMAP_ERR_CFG;
 
 out:
-	if (windomain != NULL)
-		idmap_free(windomain);
-	if (winname != NULL)
-		idmap_free(winname);
-	if (unixname != NULL)
-		idmap_free(unixname);
 	if (sql != NULL)
 		sqlite_freemem(sql);
 	return (retcode);
@@ -799,10 +772,8 @@
 	char		*s_unixname = NULL;
 	char		buf[80];
 
-	if (rule->direction < 0 &&
-			rule->windomain.idmap_utf8str_len < 1 &&
-			rule->winname.idmap_utf8str_len < 1 &&
-			rule->unixname.idmap_utf8str_len < 1)
+	if (rule->direction < 0 && EMPTY_STRING(rule->windomain) &&
+	    EMPTY_STRING(rule->winname) && EMPTY_STRING(rule->unixname))
 		return (IDMAP_SUCCESS);
 
 	if (rule->direction < 0) {
@@ -819,24 +790,21 @@
 	}
 
 	retcode = IDMAP_ERR_INTERNAL;
-	if (rule->windomain.idmap_utf8str_len > 0) {
+	if (!EMPTY_STRING(rule->windomain)) {
 		if (gen_sql_expr_from_utf8str("AND", "windomain", "=",
-				&rule->windomain,
-				"", &s_windomain) != IDMAP_SUCCESS)
+			rule->windomain, "", &s_windomain) != IDMAP_SUCCESS)
 			goto out;
 	}
 
-	if (rule->winname.idmap_utf8str_len > 0) {
+	if (!EMPTY_STRING(rule->winname)) {
 		if (gen_sql_expr_from_utf8str("AND", "winname", "=",
-				&rule->winname,
-				"", &s_winname) != IDMAP_SUCCESS)
+			rule->winname, "", &s_winname) != IDMAP_SUCCESS)
 			goto out;
 	}
 
-	if (rule->unixname.idmap_utf8str_len > 0) {
+	if (!EMPTY_STRING(rule->unixname)) {
 		if (gen_sql_expr_from_utf8str("AND", "unixname", "=",
-				&rule->unixname,
-				"", &s_unixname) != IDMAP_SUCCESS)
+			rule->unixname,	"", &s_unixname) != IDMAP_SUCCESS)
 			goto out;
 	}
 
@@ -1094,7 +1062,6 @@
 	sqlite_vm	*vm = NULL;
 	int		ncol, is_user;
 	uid_t		pid;
-	idmap_utf8str	*str;
 	time_t		curtime, exp;
 	idmap_retcode	retcode;
 
@@ -1199,9 +1166,8 @@
 			res->direction = IDMAP_DIRECTION_W2U;
 
 		if (values[3] != NULL) {
-			str = &req->id2name;
-			retcode = idmap_str2utf8(&str, values[3], 0);
-			if (retcode != IDMAP_SUCCESS) {
+			req->id2name = strdup(values[3]);
+			if (req->id2name == NULL) {
 				idmapdlog(LOG_ERR, "Out of memory");
 				retcode = IDMAP_ERR_MEMORY;
 			}
@@ -1320,7 +1286,6 @@
 	char		*sidprefix;
 	idmap_rid_t	rid;
 	char		*name = NULL, *domain = NULL;
-	idmap_utf8str	*str;
 
 	sidprefix = req->id1.idmap_id_u.sid.prefix;
 	rid = req->id1.idmap_id_u.sid.rid;
@@ -1342,14 +1307,10 @@
 		retcode = verify_type(req->id2.idtype, type, res);
 
 		/* update state in 'req' */
-		if (name != NULL) {
-			str = &req->id1name;
-			(void) idmap_str2utf8(&str, name, 1);
-		}
-		if (domain != NULL) {
-			str = &req->id1domain;
-			(void) idmap_str2utf8(&str, domain, 1);
-		}
+		if (name != NULL)
+			req->id1name = name;
+		if (domain != NULL)
+			req->id1domain = domain;
 	} else {
 		if (name != NULL)
 			free(name);
@@ -1394,8 +1355,8 @@
 					state->ad_lookup,
 					req->id1.idmap_id_u.sid.prefix,
 					&req->id1.idmap_id_u.sid.rid,
-					&req->id1name.idmap_utf8str_val,
-					&req->id1domain.idmap_utf8str_val,
+					&req->id1name,
+					&req->id1domain,
 					(int *)&res->id.idtype,
 					&res->retcode);
 
@@ -1470,8 +1431,8 @@
 	/*
 	 * Check if we already have the name (i.e name2pid lookups)
 	 */
-	if (req->id1name.idmap_utf8str_val != NULL &&
-	    req->id1domain.idmap_utf8str_val != NULL) {
+	if (req->id1name != NULL &&
+	    req->id1domain != NULL) {
 		retcode = IDMAP_SUCCESS;
 		req->direction |= _IDMAP_F_S2N_CACHE;
 		goto out;
@@ -1677,12 +1638,11 @@
 	char		*end;
 	const char	**values;
 	sqlite_vm	*vm = NULL;
-	idmap_utf8str	*str;
 	int		ncol, r, i, is_user;
 	const char	*me = "name_based_mapping_sid2pid";
 
-	winname = req->id1name.idmap_utf8str_val;
-	windomain = req->id1domain.idmap_utf8str_val;
+	winname = req->id1name;
+	windomain = req->id1domain;
 	is_user = (res->id.idtype == IDMAP_UID)?1:0;
 
 	i = 0;
@@ -1776,8 +1736,7 @@
 			    IDMAP_DIRECTION_W2U:IDMAP_DIRECTION_BI;
 		else
 			res->direction = IDMAP_DIRECTION_W2U;
-		str = &req->id2name;
-		retcode = idmap_str2utf8(&str, unixname, 0);
+		req->id2name = strdup(unixname);
 	}
 	if (vm != NULL)
 		(void) sqlite_finalize(vm, NULL);
@@ -2026,10 +1985,10 @@
 		"strftime('%%s','now') + 600, %q, 1); ",
 		res->id.idmap_id_u.sid.prefix,
 		res->id.idmap_id_u.sid.rid,
-		req->id2domain.idmap_utf8str_val,
-		req->id2name.idmap_utf8str_val,
+		req->id2domain,
+		req->id2name,
 		req->id1.idmap_id_u.uid,
-		req->id1name.idmap_utf8str_val,
+		req->id1name,
 		(req->id1.idtype == IDMAP_UID)?1:0,
 		(res->direction == 0)?"1":NULL);
 
@@ -2051,7 +2010,7 @@
 	if (req->direction & _IDMAP_F_S2N_CACHE)
 		goto out;
 
-	if (req->id2name.idmap_utf8str_val == NULL)
+	if (req->id2name == NULL)
 		goto out;
 
 	sql = sqlite_mprintf("INSERT OR REPLACE into name_cache "
@@ -2059,8 +2018,8 @@
 		"VALUES(%Q, %u, %Q, %Q, %d, strftime('%%s','now') + 3600); ",
 		res->id.idmap_id_u.sid.prefix,
 		res->id.idmap_id_u.sid.rid,
-		req->id2name.idmap_utf8str_val,
-		req->id2domain.idmap_utf8str_val,
+		req->id2name,
+		req->id2domain,
 		(req->id1.idtype == IDMAP_UID)?_IDMAP_T_USER:_IDMAP_T_GROUP);
 
 	if (sql == NULL) {
@@ -2128,10 +2087,10 @@
 		"strftime('%%s','now') + 600, 1, %q); ",
 		req->id1.idmap_id_u.sid.prefix,
 		req->id1.idmap_id_u.sid.rid,
-		req->id1domain.idmap_utf8str_val,
-		req->id1name.idmap_utf8str_val,
+		req->id1domain,
+		req->id1name,
 		res->id.idmap_id_u.uid,
-		req->id2name.idmap_utf8str_val,
+		req->id2name,
 		(res->id.idtype == IDMAP_UID)?1:0,
 		(res->direction == 0)?"1":NULL);
 
@@ -2153,7 +2112,7 @@
 	if (req->direction & _IDMAP_F_S2N_CACHE)
 		goto out;
 
-	if (req->id1name.idmap_utf8str_val == NULL)
+	if (req->id1name == NULL)
 		goto out;
 
 	sql = sqlite_mprintf("INSERT OR REPLACE into name_cache "
@@ -2161,8 +2120,8 @@
 		"VALUES(%Q, %u, %Q, %Q, %d, strftime('%%s','now') + 3600); ",
 		req->id1.idmap_id_u.sid.prefix,
 		req->id1.idmap_id_u.sid.rid,
-		req->id1name.idmap_utf8str_val,
-		req->id1domain.idmap_utf8str_val,
+		req->id1name,
+		req->id1domain,
 		(res->id.idtype == IDMAP_UID)?_IDMAP_T_USER:_IDMAP_T_GROUP);
 
 	if (sql == NULL) {
@@ -2188,7 +2147,6 @@
 	sqlite_vm	*vm = NULL;
 	int		ncol;
 	idmap_retcode	retcode = IDMAP_SUCCESS;
-	idmap_utf8str	*str;
 	time_t		curtime;
 
 	/* Current time */
@@ -2246,9 +2204,8 @@
 
 			if (getname == 0 || values[2] == NULL)
 				break;
-			str = &req->id2name;
-			retcode = idmap_str2utf8(&str, values[2], 0);
-			if (retcode != IDMAP_SUCCESS) {
+			req->id2name = strdup(values[2]);
+			if (req->id2name == NULL) {
 				idmapdlog(LOG_ERR, "Out of memory");
 				retcode = IDMAP_ERR_MEMORY;
 				goto out;
@@ -2256,9 +2213,8 @@
 
 			if (values[3] == NULL)
 				break;
-			str = &req->id2domain;
-			retcode = idmap_str2utf8(&str, values[3], 0);
-			if (retcode != IDMAP_SUCCESS) {
+			req->id2domain = strdup(values[3]);
+			if (req->id2domain == NULL) {
 				idmapdlog(LOG_ERR, "Out of memory");
 				retcode = IDMAP_ERR_MEMORY;
 				goto out;
@@ -2453,7 +2409,6 @@
 	char		*end;
 	const char	**values;
 	sqlite_vm	*vm = NULL;
-	idmap_utf8str	*str;
 	int		ncol, r;
 	const char	*me = "name_based_mapping_pid2sid";
 
@@ -2555,15 +2510,15 @@
 			    IDMAP_DIRECTION_U2W:IDMAP_DIRECTION_BI;
 		else
 			res->direction = IDMAP_DIRECTION_U2W;
-		str = &req->id2name;
-		retcode = idmap_str2utf8(&str, winname, 0);
-		if (retcode == IDMAP_SUCCESS) {
-			str = &req->id2domain;
+
+		req->id2name = strdup(winname);
+		if (req->id2name != NULL) {
 			if (windomain == mapping_domain) {
-				(void) idmap_str2utf8(&str, windomain, 1);
+				req->id2domain = (char *)windomain;
 				mapping_domain = NULL;
-			} else
-				retcode = idmap_str2utf8(&str, windomain, 0);
+			} else {
+				req->id2domain = strdup(windomain);
+			}
 		}
 	}
 	if (vm != NULL)
@@ -2580,7 +2535,6 @@
 	char		*unixname = NULL;
 	struct passwd	pwd;
 	struct group	grp;
-	idmap_utf8str	*str;
 	char		buf[1024];
 	int		errnum;
 	idmap_retcode	retcode = IDMAP_SUCCESS;
@@ -2611,9 +2565,9 @@
 	}
 
 	/* uid/gid to name */
-	if (req->id1name.idmap_utf8str_val != NULL) {
-		unixname = req->id1name.idmap_utf8str_val;
-	} if (is_user) {
+	if (req->id1name != NULL) {
+		unixname = req->id1name;
+	} else if (is_user) {
 		errno = 0;
 		if (getpwuid_r(req->id1.idmap_id_u.uid, &pwd, buf,
 				sizeof (buf)) == NULL) {
@@ -2660,12 +2614,9 @@
 	(void) generate_localsid(req, res, is_user);
 
 out:
-	if (retcode == IDMAP_SUCCESS) {
-		if (req->id1name.idmap_utf8str_val == NULL &&
-		    unixname != NULL) {
-			str = &req->id1name;
-			retcode = idmap_str2utf8(&str, unixname, 0);
-		}
+	if (retcode == IDMAP_SUCCESS && req->id1name == NULL &&
+	    unixname != NULL) {
+		req->id1name = strdup(unixname);
 	}
 	if (req->direction != _IDMAP_F_DONE)
 		state->pid2sid_done = FALSE;
@@ -2730,50 +2681,30 @@
 			mapping->id1.idmap_id_u.sid.prefix =
 			    strdup(request->id1.idmap_id_u.sid.prefix);
 			if (mapping->id1.idmap_id_u.sid.prefix == NULL)
-				return (-1);
+				goto errout;
 		}
 	} else {
 		mapping->id1.idmap_id_u.uid = request->id1.idmap_id_u.uid;
 	}
 
-	mapping->id1domain.idmap_utf8str_len =
-	    request->id1domain.idmap_utf8str_len;
-	if (mapping->id1domain.idmap_utf8str_len) {
-		mapping->id1domain.idmap_utf8str_val =
-		    strdup(request->id1domain.idmap_utf8str_val);
-		if (mapping->id1domain.idmap_utf8str_val == NULL)
-			return (-1);
-	}
+	mapping->id1domain = strdup(request->id1domain);
+	if (mapping->id1domain == NULL)
+		goto errout;
 
-	mapping->id1name.idmap_utf8str_len  =
-	    request->id1name.idmap_utf8str_len;
-	if (mapping->id1name.idmap_utf8str_len) {
-		mapping->id1name.idmap_utf8str_val =
-		    strdup(request->id1name.idmap_utf8str_val);
-		if (mapping->id1name.idmap_utf8str_val == NULL)
-			return (-1);
-	}
+	mapping->id1name = strdup(request->id1name);
+	if (mapping->id1name == NULL)
+		goto errout;
 
 	/* We don't need the rest of the request i.e request->id2 */
 	return (0);
 
 errout:
-	if (mapping->id1.idmap_id_u.sid.prefix != NULL) {
+	if (mapping->id1.idmap_id_u.sid.prefix != NULL)
 		free(mapping->id1.idmap_id_u.sid.prefix);
-		mapping->id1.idmap_id_u.sid.prefix = NULL;
-	}
-
-	if (mapping->id1domain.idmap_utf8str_val != NULL) {
-		free(mapping->id1domain.idmap_utf8str_val);
-		mapping->id1domain.idmap_utf8str_val = NULL;
-		mapping->id1domain.idmap_utf8str_len = 0;
-	}
-
-	if (mapping->id1name.idmap_utf8str_val != NULL) {
-		free(mapping->id1name.idmap_utf8str_val);
-		mapping->id1name.idmap_utf8str_val = NULL;
-		mapping->id1name.idmap_utf8str_len = 0;
-	}
+	if (mapping->id1domain != NULL)
+		free(mapping->id1domain);
+	if (mapping->id1name != NULL)
+		free(mapping->id1name);
 
 	(void) memset(mapping, 0, sizeof (*mapping));
 	return (-1);
@@ -2785,7 +2716,6 @@
 		idmap_mapping *mapping) {
 	idmap_id_res	idres;
 	lookup_state_t	state;
-	idmap_utf8str	*str;
 	char		*cp;
 	int		is_user;
 	idmap_retcode	retcode;
@@ -2811,8 +2741,8 @@
 		goto out;
 	}
 
-	winname = mapping->id1name.idmap_utf8str_val;
-	windomain = mapping->id1domain.idmap_utf8str_val;
+	winname = mapping->id1name;
+	windomain = mapping->id1domain;
 
 	if (winname == NULL && windomain != NULL) {
 		retcode = IDMAP_ERR_ARG;
@@ -2820,30 +2750,35 @@
 	}
 
 	if (winname != NULL && windomain == NULL) {
-		str = &mapping->id1domain;
-
+		retcode = IDMAP_SUCCESS;
 		if ((cp = strchr(winname, '@')) != NULL) {
 			/*
 			 * if winname is qualified with a domain, use it.
 			 */
 			*cp = '\0';
-			retcode = idmap_str2utf8(&str, cp + 1, 0);
-		} else if (_idmapdstate.cfg->pgcfg.mapping_domain != NULL) {
+			mapping->id1domain = strdup(cp + 1);
+			if (mapping->id1domain == NULL)
+				retcode = IDMAP_ERR_MEMORY;
+		} else {
+			RDLOCK_CONFIG();
+			if (_idmapdstate.cfg->pgcfg.mapping_domain != NULL) {
 			/*
 			 * otherwise use the mapping domain
 			 */
-			RDLOCK_CONFIG();
-			retcode = idmap_str2utf8(&str,
-				_idmapdstate.cfg->pgcfg.mapping_domain, 0);
+				mapping->id1domain =
+				    strdup(_idmapdstate.cfg->
+					pgcfg.mapping_domain);
+				if (mapping->id1domain == NULL)
+					retcode = IDMAP_ERR_MEMORY;
+			}
 			UNLOCK_CONFIG();
-		} else
-			retcode = IDMAP_SUCCESS;
+		}
 
 		if (retcode != IDMAP_SUCCESS) {
 			idmapdlog(LOG_ERR, "Out of memory");
 			goto out;
 		}
-		windomain = mapping->id1domain.idmap_utf8str_val;
+		windomain = mapping->id1domain;
 	}
 
 	if (winname != NULL && mapping->id1.idmap_id_u.sid.prefix == NULL) {
@@ -2866,8 +2801,8 @@
 		retcode = lookup_win_sid2name(
 			mapping->id1.idmap_id_u.sid.prefix,
 			mapping->id1.idmap_id_u.sid.rid,
-			&mapping->id1name.idmap_utf8str_val,
-			&mapping->id1domain.idmap_utf8str_val,
+			&mapping->id1name,
+			&mapping->id1domain,
 			(int *)&idres.id.idtype);
 
 		idres.retcode = retcode;
@@ -2921,7 +2856,7 @@
 		goto out;
 	}
 
-	unixname = mapping->id1name.idmap_utf8str_val;
+	unixname = mapping->id1name;
 
 	if (unixname == NULL && mapping->id1.idmap_id_u.uid == SENTINEL_PID) {
 		retcode = IDMAP_ERR_ARG;
--- a/usr/src/cmd/idmap/idmapd/idmapd.c	Mon Sep 17 08:01:53 2007 -0700
+++ b/usr/src/cmd/idmap/idmapd/idmapd.c	Mon Sep 17 08:07:28 2007 -0700
@@ -263,6 +263,7 @@
 static void
 init_idmapd() {
 	int	error;
+	int connmaxrec = RPC_MAX_SIZE;
 
 	/* create directories as root and chown to daemon uid */
 	if (create_directory(IDMAP_DBDIR, DAEMON_UID, DAEMON_GID) < 0)
@@ -309,6 +310,12 @@
 		goto errout;
 	}
 
+	if (!svc_control(xprt, SVCSET_CONNMAXREC, &connmaxrec)) {
+		idmapdlog(LOG_ERR,
+		    "idmapd: unable to limit RPC request size");
+		goto errout;
+	}
+
 	dfd = xprt->xp_fd;
 
 	if (dfd == -1) {
--- a/usr/src/cmd/idmap/idmapd/idmapd.h	Mon Sep 17 08:01:53 2007 -0700
+++ b/usr/src/cmd/idmap/idmapd/idmapd.h	Mon Sep 17 08:07:28 2007 -0700
@@ -140,6 +140,10 @@
 #define	IDMAP_CACHEDIR	"/var/run/idmap"
 #define	IDMAP_DBNAME	IDMAP_DBDIR "/idmap.db"
 #define	IDMAP_CACHENAME	IDMAP_CACHEDIR "/idmap.db"
+#define	IDMAP_CACHENAME	IDMAP_CACHEDIR "/idmap.db"
+#define	RPC_MAX_SIZE	65536
+
+#define	EMPTY_STRING(str)	(str == NULL || *str == 0)
 
 typedef idmap_retcode (*update_list_res_cb)(void *, const char **, uint64_t);
 typedef int (*list_svc_cb)(void *, int, char **, char **);
@@ -165,7 +169,7 @@
 
 extern idmap_retcode	gen_sql_expr_from_utf8str(const char *,
 				const char *, const char *,
-				idmap_utf8str *, const char *,
+				char *, const char *,
 				char **);
 extern idmap_retcode	validate_list_cb_data(list_cb_data_t *, int,
 				char **, int, uchar_t **, size_t);
--- a/usr/src/cmd/idmap/idmapd/rpc_svc.c	Mon Sep 17 08:01:53 2007 -0700
+++ b/usr/src/cmd/idmap/idmapd/rpc_svc.c	Mon Sep 17 08:07:28 2007 -0700
@@ -69,9 +69,9 @@
 }
 
 int
-_idmap_update_1(idmap_update_batch  *argp, idmap_retcode *result,
+_idmap_update_1(idmap_update_batch  *argp, idmap_update_res *res,
 		struct svc_req *rqstp) {
-	return (idmap_update_1_svc(*argp, result, rqstp));
+	return (idmap_update_1_svc(*argp, res, rqstp));
 }
 
 int
@@ -95,7 +95,7 @@
 		idmap_ids_res idmap_get_mapped_ids_1_res;
 		idmap_mappings_res idmap_list_mappings_1_res;
 		idmap_namerules_res idmap_list_namerules_1_res;
-		idmap_retcode idmap_update_1_res;
+		idmap_update_res idmap_update_1_res;
 		idmap_mappings_res idmap_get_mapped_id_by_name_1_res;
 	} result;
 	bool_t retval;
@@ -136,7 +136,7 @@
 
 	case IDMAP_UPDATE:
 		_xdr_argument = (xdrproc_t)xdr_idmap_update_batch;
-		_xdr_result = (xdrproc_t)xdr_idmap_retcode;
+		_xdr_result = (xdrproc_t)xdr_idmap_update_res;
 		local = (bool_t (*) (char *,  void *,  struct svc_req *))
 		    _idmap_update_1;
 		break;
--- a/usr/src/cmd/idmap/idmapd/server.c	Mon Sep 17 08:01:53 2007 -0700
+++ b/usr/src/cmd/idmap/idmapd/server.c	Mon Sep 17 08:07:28 2007 -0700
@@ -63,6 +63,14 @@
 		res->retcode = IDMAP_ERR_NOTFOUND;
 
 
+#define	STRDUP_OR_FAIL(to, from) \
+	if ((from) == NULL) \
+		to = NULL; \
+	else { \
+		if ((to = strdup(from)) == NULL) \
+			return (1); \
+	}
+
 /* ARGSUSED */
 bool_t
 idmap_null_1_svc(void *result, struct svc_req *rqstp) {
@@ -268,7 +276,6 @@
 	list_cb_data_t		*cb_data;
 	char			*str;
 	idmap_mappings_res	*result;
-	idmap_utf8str		*ptr;
 	idmap_retcode		retcode;
 	int			w2u, u2w;
 	char			*end;
@@ -306,17 +313,15 @@
 		result->mappings.mappings_val[cb_data->next].direction =
 		    IDMAP_DIRECTION_BI;
 
-	ptr = &result->mappings.mappings_val[cb_data->next].id1domain;
-	if (idmap_str2utf8(&ptr, argv[6], 0) != IDMAP_SUCCESS)
-		return (1);
+	STRDUP_OR_FAIL(result->mappings.mappings_val[cb_data->next].id1domain,
+	    argv[6]);
 
-	ptr = &result->mappings.mappings_val[cb_data->next].id1name;
-	if (idmap_str2utf8(&ptr, argv[7], 0) != IDMAP_SUCCESS)
-		return (1);
+	STRDUP_OR_FAIL(result->mappings.mappings_val[cb_data->next].id1name,
+	    argv[7]);
 
-	ptr = &result->mappings.mappings_val[cb_data->next].id2name;
-	if (idmap_str2utf8(&ptr, argv[8], 0) != IDMAP_SUCCESS)
-		return (1);
+	STRDUP_OR_FAIL(result->mappings.mappings_val[cb_data->next].id2name,
+	    argv[8]);
+
 
 	result->lastrowid = strtoll(argv[0], &end, 10);
 	cb_data->next++;
@@ -393,7 +398,6 @@
 	list_cb_data_t		*cb_data;
 	idmap_namerules_res	*result;
 	idmap_retcode		retcode;
-	idmap_utf8str		*ptr;
 	int			w2u_order, u2w_order;
 	char			*end;
 
@@ -408,20 +412,17 @@
 	result->rules.rules_val[cb_data->next].is_user =
 		strtol(argv[1], &end, 10);
 
-	ptr = &result->rules.rules_val[cb_data->next].windomain;
-	if (idmap_str2utf8(&ptr, argv[2], 0) != IDMAP_SUCCESS)
-		return (1);
+	STRDUP_OR_FAIL(result->rules.rules_val[cb_data->next].windomain,
+	    argv[2]);
 
-	ptr = &result->rules.rules_val[cb_data->next].winname;
-	if (idmap_str2utf8(&ptr, argv[3], 0) != IDMAP_SUCCESS)
-		return (1);
+	STRDUP_OR_FAIL(result->rules.rules_val[cb_data->next].winname,
+	    argv[3]);
 
 	result->rules.rules_val[cb_data->next].is_nt4 =
 		strtol(argv[4], &end, 10);
 
-	ptr = &result->rules.rules_val[cb_data->next].unixname;
-	if (idmap_str2utf8(&ptr, argv[5], 0) != IDMAP_SUCCESS)
-		return (1);
+	STRDUP_OR_FAIL(result->rules.rules_val[cb_data->next].unixname,
+	    argv[5]);
 
 	w2u_order = argv[6]?strtol(argv[6], &end, 10):0;
 	u2w_order = argv[7]?strtol(argv[7], &end, 10):0;
@@ -488,25 +489,25 @@
 	}
 
 	/* Create where statement for windomain */
-	if (rule.windomain.idmap_utf8str_len > 0) {
+	if (!EMPTY_STRING(rule.windomain)) {
 		if (gen_sql_expr_from_utf8str("AND", "windomain", "=",
-				&rule.windomain,
+				rule.windomain,
 				"", &s_windomain) != IDMAP_SUCCESS)
 			goto out;
 	}
 
 	/* Create where statement for winname */
-	if (rule.winname.idmap_utf8str_len > 0) {
+	if (!EMPTY_STRING(rule.winname)) {
 		if (gen_sql_expr_from_utf8str("AND", "winname", "=",
-				&rule.winname,
+				rule.winname,
 				"", &s_winname) != IDMAP_SUCCESS)
 			goto out;
 	}
 
 	/* Create where statement for unixname */
-	if (rule.unixname.idmap_utf8str_len > 0) {
+	if (!EMPTY_STRING(rule.unixname)) {
 		if (gen_sql_expr_from_utf8str("AND", "unixname", "=",
-				&rule.unixname,
+				rule.unixname,
 				"", &s_unixname) != IDMAP_SUCCESS)
 			goto out;
 	}
@@ -602,33 +603,45 @@
 	return (1);
 }
 
+/*
+ * Meaning of the return values is the following: For retcode ==
+ * IDMAP_SUCCESS, everything went OK and error_index is
+ * undefined. Otherwise, error_index >=0 shows the failed batch
+ * element. errro_index == -1 indicates failure at the beginning,
+ * error_index == -2 at the end.
+ */
+
 /* ARGSUSED */
 bool_t
-idmap_update_1_svc(idmap_update_batch batch, idmap_retcode *result,
+idmap_update_1_svc(idmap_update_batch batch, idmap_update_res *res,
 		struct svc_req *rqstp) {
 	sqlite		*db = NULL;
 	idmap_update_op	*up;
 	int		i;
 	int		trans = FALSE;
 
+	res->error_index = -1;
+	(void) memset(&res->error_rule, 0, sizeof (res->error_rule));
+	(void) memset(&res->conflict_rule, 0, sizeof (res->conflict_rule));
+
 	if (verify_rules_auth(rqstp) < 0) {
-		*result = IDMAP_ERR_PERMISSION_DENIED;
+		res->retcode = IDMAP_ERR_PERMISSION_DENIED;
 		goto out;
 	}
 
 	if (batch.idmap_update_batch_len == 0 ||
 			batch.idmap_update_batch_val == NULL) {
-		*result = IDMAP_SUCCESS;
+		res->retcode = IDMAP_SUCCESS;
 		goto out;
 	}
 
 	/* Get db handle */
-	*result = get_db_handle(&db);
-	if (*result != IDMAP_SUCCESS)
+	res->retcode = get_db_handle(&db);
+	if (res->retcode != IDMAP_SUCCESS)
 		goto out;
 
-	*result = sql_exec_no_cb(db, "BEGIN TRANSACTION;");
-	if (*result != IDMAP_SUCCESS)
+	res->retcode = sql_exec_no_cb(db, "BEGIN TRANSACTION;");
+	if (res->retcode != IDMAP_SUCCESS)
 		goto out;
 	trans = TRUE;
 
@@ -636,37 +649,53 @@
 		up = &batch.idmap_update_batch_val[i];
 		switch (up->opnum) {
 		case OP_NONE:
-			*result = IDMAP_SUCCESS;
+			res->retcode = IDMAP_SUCCESS;
 			break;
 		case OP_ADD_NAMERULE:
-			*result = add_namerule(db,
+			res->retcode = add_namerule(db,
 				&up->idmap_update_op_u.rule);
 			break;
 		case OP_RM_NAMERULE:
-			*result = rm_namerule(db,
+			res->retcode = rm_namerule(db,
 				&up->idmap_update_op_u.rule);
 			break;
 		case OP_FLUSH_NAMERULES:
-			*result = flush_namerules(db,
+			res->retcode = flush_namerules(db,
 				up->idmap_update_op_u.is_user);
 			break;
 		default:
-			*result = IDMAP_ERR_NOTSUPPORTED;
-			goto out;
+			res->retcode = IDMAP_ERR_NOTSUPPORTED;
+			break;
 		};
 
-		if (*result != IDMAP_SUCCESS)
+		if (res->retcode != IDMAP_SUCCESS) {
+			res->error_index = i;
+			if (up->opnum == OP_ADD_NAMERULE ||
+			    up->opnum == OP_RM_NAMERULE) {
+				idmap_stat r2 =
+				    idmap_namerule_cpy(&res->error_rule,
+					&up->idmap_update_op_u.rule);
+				if (r2 != IDMAP_SUCCESS)
+					res->retcode = r2;
+			}
 			goto out;
+		}
 	}
 
 out:
 	if (trans) {
-		if (*result == IDMAP_SUCCESS)
-			*result = sql_exec_no_cb(db, "COMMIT TRANSACTION;");
+		if (res->retcode == IDMAP_SUCCESS) {
+			res->retcode =
+			    sql_exec_no_cb(db, "COMMIT TRANSACTION;");
+			if (res->retcode !=  IDMAP_SUCCESS)
+				res->error_index = -2;
+		}
 		else
 			(void) sql_exec_no_cb(db, "ROLLBACK TRANSACTION;");
 	}
-	*result = idmap_stat4prot(*result);
+
+	res->retcode = idmap_stat4prot(res->retcode);
+
 	return (TRUE);
 }
 
--- a/usr/src/head/rpcsvc/idmap_prot.x	Mon Sep 17 08:01:53 2007 -0700
+++ b/usr/src/head/rpcsvc/idmap_prot.x	Mon Sep 17 08:07:28 2007 -0700
@@ -26,7 +26,7 @@
 %#pragma ident	"%Z%%M%	%I%	%E% SMI"
 
 /* opaque type to support non-ASCII strings */
-typedef	opaque	idmap_utf8str<>;
+typedef	string	idmap_utf8str<>;
 
 /* Return status */
 typedef int idmap_retcode;
@@ -109,6 +109,13 @@
 	idmap_namerule	rules<>;
 };
 
+struct idmap_update_res {
+	idmap_retcode	retcode;
+	int64_t	error_index;
+	idmap_namerule	error_rule;
+	idmap_namerule	conflict_rule;
+};
+
 
 /* Update requests */
 enum idmap_opnum {
@@ -149,7 +156,7 @@
 			uint64_t lastrowid, uint64_t limit) = 3;
 
 		/* Batch of update requests */
-		idmap_retcode
+		idmap_update_res
 		IDMAP_UPDATE(idmap_update_batch batch) = 4;
 
 		/* Get mapped identity by name */
--- a/usr/src/lib/libidmap/common/idmap_api.c	Mon Sep 17 08:01:53 2007 -0700
+++ b/usr/src/lib/libidmap/common/idmap_api.c	Mon Sep 17 08:07:28 2007 -0700
@@ -44,6 +44,7 @@
 static struct timeval TIMEOUT = { 25, 0 };
 
 static int idmap_stat2errno(idmap_stat);
+static idmap_stat idmap_strdupnull(char **, const char *);
 
 #define	__ITER_CREATE(itera, argu, handl, ityp)\
 	if (handl == NULL) {\
@@ -88,6 +89,7 @@
 		return (IDMAP_ERR_ARG);\
 	}
 
+#define	EMPTY_STRING(str)	(str == NULL || *str == '\0')
 
 /*
  * Free memory allocated by libidmap API
@@ -202,26 +204,217 @@
 idmap_udt_commit(idmap_udt_handle_t *udthandle) {
 	CLIENT			*clnt;
 	enum clnt_stat		clntstat;
-	idmap_retcode		retcode;
+	idmap_update_res	res;
+	idmap_stat		retcode;
 
 	if (udthandle == NULL) {
 		errno = EINVAL;
 		return (IDMAP_ERR_ARG);
 	}
+
+	(void) memset(&res, 0, sizeof (res));
+
 	_IDMAP_GET_CLIENT_HANDLE(udthandle->ih, clnt);
 	clntstat = clnt_call(clnt, IDMAP_UPDATE,
 		(xdrproc_t)xdr_idmap_update_batch, (caddr_t)&udthandle->batch,
-		(xdrproc_t)xdr_idmap_retcode, (caddr_t)&retcode,
+		(xdrproc_t)xdr_idmap_update_res, (caddr_t)&res,
 		TIMEOUT);
 
+	if (clntstat != RPC_SUCCESS) {
+		retcode = _idmap_rpc2stat(clnt);
+		goto out;
+	}
+
+	retcode = udthandle->commit_stat = res.retcode;
+	udthandle->error_index = res.error_index;
+
+	if (retcode != IDMAP_SUCCESS) {
+
+		if (udthandle->error_index < 0)
+			goto out;
+
+		retcode = idmap_namerule_cpy(&udthandle->error_rule,
+		    &res.error_rule);
+		if (retcode != IDMAP_SUCCESS) {
+			udthandle->error_index = -2;
+			goto out;
+		}
+
+		retcode = idmap_namerule_cpy(&udthandle->conflict_rule,
+		    &res.conflict_rule);
+		if (retcode != IDMAP_SUCCESS) {
+			udthandle->error_index = -2;
+			goto out;
+		}
+	}
+
+	retcode = res.retcode;
+
+
+out:
 	/* reset handle so that it can be used again */
-	_IDMAP_RESET_UDT_HANDLE(udthandle);
+	if (retcode == IDMAP_SUCCESS) {
+		_IDMAP_RESET_UDT_HANDLE(udthandle);
+	}
+
+	(void) xdr_free(xdr_idmap_update_res, (caddr_t)&res);
+	errno = idmap_stat2errno(retcode);
+	return (retcode);
+}
+
+
+static void
+idmap_namerule_parts_clear(char **windomain, char **winname,
+    char **unixname, boolean_t *is_user, boolean_t *is_nt4,
+    int *direction) {
+	if (windomain)
+		*windomain = NULL;
+	if (winname)
+		*winname = NULL;
+	if (unixname)
+		*unixname = NULL;
+
+	if (is_nt4)
+		*is_nt4 = 0;
+	if (is_user)
+		*is_user = -1;
+	if (direction)
+		*direction = IDMAP_DIRECTION_UNDEF;
+}
+
+static idmap_stat
+idmap_namerule2parts(idmap_namerule	*rule,
+    char **windomain, char **winname,
+    char **unixname, boolean_t *is_user, boolean_t *is_nt4,
+    int *direction) {
+	idmap_stat retcode;
+
+	if (EMPTY_STRING(rule->winname) && EMPTY_STRING(rule->unixname))
+		return (IDMAP_ERR_NORESULT);
+
+
+	retcode = idmap_strdupnull(windomain, rule->windomain);
+	if (retcode != IDMAP_SUCCESS)
+		goto errout;
+
+	retcode = idmap_strdupnull(winname, rule->winname);
+	if (retcode != IDMAP_SUCCESS)
+		goto errout;
+
+	retcode = idmap_strdupnull(unixname, rule->unixname);
+	if (retcode != IDMAP_SUCCESS)
+		goto errout;
+
+
+	if (is_user)
+		*is_user = rule->is_user;
+	if (is_nt4)
+		*is_nt4 = rule->is_nt4;
+	if (direction)
+		*direction = rule->direction;
+
+
+	return (IDMAP_SUCCESS);
 
-	if (clntstat != RPC_SUCCESS)
-		return (_idmap_rpc2stat(clnt));
-	if (retcode != IDMAP_SUCCESS)
-		errno = idmap_stat2errno(retcode);
+errout:
+	if (windomain && *windomain)
+		free(*windomain);
+	if (winname && *winname)
+		free(*winname);
+	if (unixname && *unixname)
+		free(*unixname);
+
+	idmap_namerule_parts_clear(windomain, winname,
+	    unixname, is_user, is_nt4, direction);
+
 	return (retcode);
+
+}
+
+/*
+ * Retrieve the index of the failed batch element. error_index == -1
+ * indicates failure at the beginning, -2 at the end.
+ *
+ * If idmap_udt_commit didn't return error, the returned value is undefined.
+ *
+ * Return value:
+ * IDMAP_SUCCESS
+ */
+
+idmap_stat
+idmap_udt_get_error_index(idmap_udt_handle_t *udthandle,
+    int64_t *error_index) {
+	if (error_index)
+		*error_index = udthandle->error_index;
+
+	return (IDMAP_SUCCESS);
+}
+
+
+/*
+ * Retrieve the rule which caused the batch to fail. If
+ * idmap_udt_commit didn't return error or if error_index is < 0, the
+ * retrieved rule is undefined.
+ *
+ * Return value:
+ * IDMAP_ERR_NORESULT if there is no error rule.
+ * IDMAP_SUCCESS if the rule was obtained OK.
+ * other error code (IDMAP_ERR_NOMEMORY etc)
+ */
+
+idmap_stat
+idmap_udt_get_error_rule(idmap_udt_handle_t *udthandle,
+    char **windomain, char **winname,
+    char **unixname, boolean_t *is_user, boolean_t *is_nt4,
+    int *direction) {
+	idmap_namerule_parts_clear(windomain, winname,
+	    unixname, is_user, is_nt4, direction);
+
+	if (udthandle->commit_stat == IDMAP_SUCCESS ||
+	    udthandle->error_index < 0)
+		return (IDMAP_ERR_NORESULT);
+
+	return (idmap_namerule2parts(
+			&udthandle->error_rule,
+			    windomain,
+			    winname,
+			    unixname,
+			    is_user,
+			    is_nt4,
+			    direction));
+}
+
+/*
+ * Retrieve the rule with which there was a conflict. TODO: retrieve
+ * the value.
+ *
+ * Return value:
+ * IDMAP_ERR_NORESULT if there is no error rule.
+ * IDMAP_SUCCESS if the rule was obtained OK.
+ * other error code (IDMAP_ERR_NOMEMORY etc)
+ */
+
+idmap_stat
+idmap_udt_get_conflict_rule(idmap_udt_handle_t *udthandle,
+    char **windomain, char **winname,
+    char **unixname, boolean_t *is_user, boolean_t *is_nt4,
+    int *direction) {
+	idmap_namerule_parts_clear(windomain, winname,
+	    unixname, is_user, is_nt4, direction);
+
+	if (udthandle->commit_stat != IDMAP_ERR_W2U_NAMERULE_CONFLICT &&
+	    udthandle->commit_stat != IDMAP_ERR_U2W_NAMERULE_CONFLICT) {
+		    return (IDMAP_ERR_NORESULT);
+	}
+
+	return (idmap_namerule2parts(
+			&udthandle->conflict_rule,
+			    windomain,
+			    winname,
+			    unixname,
+			    is_user,
+			    is_nt4,
+			    direction));
 }
 
 
@@ -233,6 +426,8 @@
 	if (udthandle == NULL)
 		return;
 	(void) xdr_free(xdr_idmap_update_batch, (caddr_t)&udthandle->batch);
+	(void) xdr_free(xdr_idmap_namerule, (caddr_t)&udthandle->error_rule);
+	(void) xdr_free(xdr_idmap_namerule, (caddr_t)&udthandle->conflict_rule);
 	free(udthandle);
 }
 
@@ -243,7 +438,6 @@
 		boolean_t is_nt4, int direction) {
 	idmap_retcode	retcode;
 	idmap_namerule	*rule = NULL;
-	idmap_utf8str	*str;
 
 	retcode = _udt_extend_batch(udthandle);
 	if (retcode != IDMAP_SUCCESS)
@@ -255,24 +449,18 @@
 	rule->is_user = is_user;
 	rule->direction = direction;
 	rule->is_nt4 = is_nt4;
-	if (windomain) {
-		str = &rule->windomain;
-		retcode = idmap_str2utf8(&str, windomain, 0);
-		if (retcode != IDMAP_SUCCESS)
-			goto errout;
-	}
-	if (winname) {
-		str = &rule->winname;
-		retcode = idmap_str2utf8(&str, winname, 0);
-		if (retcode != IDMAP_SUCCESS)
-			goto errout;
-	}
-	if (unixname) {
-		str = &rule->unixname;
-		retcode = idmap_str2utf8(&str, unixname, 0);
-		if (retcode != IDMAP_SUCCESS)
-			goto errout;
-	}
+
+	retcode = idmap_strdupnull(&rule->windomain, windomain);
+	if (retcode != IDMAP_SUCCESS)
+		goto errout;
+
+	retcode = idmap_strdupnull(&rule->winname, winname);
+	if (retcode != IDMAP_SUCCESS)
+		goto errout;
+
+	retcode = idmap_strdupnull(&rule->unixname, unixname);
+	if (retcode != IDMAP_SUCCESS)
+		goto errout;
 
 	udthandle->batch.idmap_update_batch_val[udthandle->next].opnum =
 	    OP_ADD_NAMERULE;
@@ -295,7 +483,6 @@
 		const char *unixname, int direction) {
 	idmap_retcode	retcode;
 	idmap_namerule	*rule = NULL;
-	idmap_utf8str	*str;
 
 	retcode = _udt_extend_batch(udthandle);
 	if (retcode != IDMAP_SUCCESS)
@@ -306,24 +493,19 @@
 		idmap_update_op_u.rule;
 	rule->is_user = is_user;
 	rule->direction = direction;
-	if (windomain) {
-		str = &rule->windomain;
-		retcode = idmap_str2utf8(&str, windomain, 0);
-		if (retcode != IDMAP_SUCCESS)
-			goto errout;
-	}
-	if (winname) {
-		str = &rule->winname;
-		retcode = idmap_str2utf8(&str, winname, 0);
-		if (retcode != IDMAP_SUCCESS)
-			goto errout;
-	}
-	if (unixname) {
-		str = &rule->unixname;
-		retcode = idmap_str2utf8(&str, unixname, 0);
-		if (retcode != IDMAP_SUCCESS)
-			goto errout;
-	}
+
+	retcode = idmap_strdupnull(&rule->windomain, windomain);
+	if (retcode != IDMAP_SUCCESS)
+		goto errout;
+
+	retcode = idmap_strdupnull(&rule->winname, winname);
+	if (retcode != IDMAP_SUCCESS)
+		goto errout;
+
+	retcode = idmap_strdupnull(&rule->unixname, unixname);
+	if (retcode != IDMAP_SUCCESS)
+		goto errout;
+
 	udthandle->batch.idmap_update_batch_val[udthandle->next].opnum =
 	    OP_RM_NAMERULE;
 	udthandle->next++;
@@ -398,7 +580,6 @@
 	idmap_iter_t			*tmpiter;
 	idmap_list_namerules_1_argument	*arg = NULL;
 	idmap_namerule			*rule;
-	idmap_utf8str			*str;
 	idmap_retcode			retcode;
 
 	__ITER_CREATE(tmpiter, arg, handle, IDMAP_LIST_NAMERULES);
@@ -406,30 +587,18 @@
 	rule = &arg->rule;
 	rule->is_user = is_user;
 	rule->direction = IDMAP_DIRECTION_UNDEF;
-	if (windomain) {
-		str = &rule->windomain;
-		retcode = idmap_str2utf8(&str, windomain, 0);
-		if (retcode != IDMAP_SUCCESS) {
-			errno = ENOMEM;
-			goto errout;
-		}
-	}
-	if (winname) {
-		str = &rule->winname;
-		retcode = idmap_str2utf8(&str, winname, 0);
-		if (retcode != IDMAP_SUCCESS) {
-			errno = ENOMEM;
-			goto errout;
-		}
-	}
-	if (unixname) {
-		str = &rule->unixname;
-		retcode = idmap_str2utf8(&str, unixname, 0);
-		if (retcode != IDMAP_SUCCESS) {
-			errno = ENOMEM;
-			goto errout;
-		}
-	}
+
+	retcode = idmap_strdupnull(&rule->windomain, windomain);
+	if (retcode != IDMAP_SUCCESS)
+		goto errout;
+
+	retcode = idmap_strdupnull(&rule->winname, winname);
+	if (retcode != IDMAP_SUCCESS)
+		goto errout;
+
+	retcode = idmap_strdupnull(&rule->unixname, unixname);
+	if (retcode != IDMAP_SUCCESS)
+		goto errout;
 
 	*iter = tmpiter;
 	return (IDMAP_SUCCESS);
@@ -515,24 +684,21 @@
 		return (IDMAP_ERR_ARG);
 	}
 
-	if (windomain) {
-		retcode = idmap_utf82str(windomain, 0,
-			&namerules->rules.rules_val[iter->next].windomain);
-		if (retcode != IDMAP_SUCCESS)
-			goto errout;
-	}
-	if (winname) {
-		retcode = idmap_utf82str(winname, 0,
-			&namerules->rules.rules_val[iter->next].winname);
-		if (retcode != IDMAP_SUCCESS)
-			goto errout;
-	}
-	if (unixname) {
-		retcode = idmap_utf82str(unixname, 0,
-			&namerules->rules.rules_val[iter->next].unixname);
-		if (retcode != IDMAP_SUCCESS)
-			goto errout;
-	}
+	retcode = idmap_strdupnull(windomain,
+	    namerules->rules.rules_val[iter->next].windomain);
+	if (retcode != IDMAP_SUCCESS)
+		goto errout;
+
+	retcode = idmap_strdupnull(winname,
+	    namerules->rules.rules_val[iter->next].winname);
+	if (retcode != IDMAP_SUCCESS)
+		goto errout;
+
+	retcode = idmap_strdupnull(unixname,
+	    namerules->rules.rules_val[iter->next].unixname);
+	if (retcode != IDMAP_SUCCESS)
+		goto errout;
+
 	if (is_nt4)
 		*is_nt4 = namerules->rules.rules_val[iter->next].is_nt4;
 	if (direction)
@@ -669,24 +835,23 @@
 	if (rid)
 		*rid = mappings->mappings.mappings_val[iter->next].id1.
 			idmap_id_u.sid.rid;
-	if (winname) {
-		retcode = idmap_utf82str(winname, 0,
-		    &mappings->mappings.mappings_val[iter->next].id1name);
-		if (retcode != IDMAP_SUCCESS)
-			goto errout;
-	}
-	if (windomain) {
-		retcode = idmap_utf82str(windomain, 0,
-		    &mappings->mappings.mappings_val[iter->next].id1domain);
-		if (retcode != IDMAP_SUCCESS)
-			goto errout;
-	}
-	if (unixname) {
-		retcode = idmap_utf82str(unixname, 0,
-		    &mappings->mappings.mappings_val[iter->next].id2name);
-		if (retcode != IDMAP_SUCCESS)
-			goto errout;
-	}
+
+	retcode = idmap_strdupnull(windomain,
+	    mappings->mappings.mappings_val[iter->next].id1domain);
+	if (retcode != IDMAP_SUCCESS)
+		goto errout;
+
+	retcode = idmap_strdupnull(winname,
+	    mappings->mappings.mappings_val[iter->next].id1name);
+	if (retcode != IDMAP_SUCCESS)
+		goto errout;
+
+	retcode = idmap_strdupnull(unixname,
+	    mappings->mappings.mappings_val[iter->next].id2name);
+	if (retcode != IDMAP_SUCCESS)
+		goto errout;
+
+
 	if (pid)
 		*pid = mappings->mappings.mappings_val[iter->next].id2.
 			idmap_id_u.uid;
@@ -1187,7 +1352,6 @@
 	idmap_mapping		request, *mapping;
 	idmap_mappings_res	result;
 	idmap_retcode		retcode, rc;
-	idmap_utf8str		*str;
 
 	if (handle == NULL) {
 		errno = EINVAL;
@@ -1214,16 +1378,14 @@
 		request.id1.idmap_id_u.sid.prefix = (char *)sidprefix;
 		request.id1.idmap_id_u.sid.rid = *rid;
 	} else if (winname) {
-		str = &request.id1name;
-		retcode = idmap_str2utf8(&str, winname, 1);
-		if (retcode != IDMAP_SUCCESS)
+		retcode = idmap_strdupnull(&request.id1name, winname);
+		if (retcode != SUCCESS)
 			goto out;
-		if (windomain) {
-			str = &request.id1domain;
-			retcode = idmap_str2utf8(&str, windomain, 1);
-			if (retcode != IDMAP_SUCCESS)
-				return (retcode);
-		}
+
+		retcode = idmap_strdupnull(&request.id1domain, windomain);
+		if (retcode != SUCCESS)
+			goto out;
+
 		request.id1.idmap_id_u.sid.prefix = NULL;
 	} else {
 		errno = EINVAL;
@@ -1266,11 +1428,10 @@
 		*direction = mapping->direction;
 	if (pid)
 		*pid = mapping->id2.idmap_id_u.uid;
-	if (unixname) {
-		rc = idmap_utf82str(unixname, 0, &mapping->id2name);
-		if (rc != IDMAP_SUCCESS)
-			retcode = rc;
-	}
+
+	rc = idmap_strdupnull(unixname, mapping->id2name);
+	if (rc != IDMAP_SUCCESS)
+		retcode = rc;
 
 out:
 	xdr_free(xdr_idmap_mappings_res, (caddr_t)&result);
@@ -1295,7 +1456,6 @@
 	idmap_mapping		request, *mapping;
 	idmap_mappings_res	result;
 	idmap_retcode		retcode, rc;
-	idmap_utf8str		*str;
 
 	if (handle == NULL) {
 		errno = EINVAL;
@@ -1324,10 +1484,7 @@
 	if (pid && *pid != UINT32_MAX) {
 		request.id1.idmap_id_u.uid = *pid;
 	} else if (unixname) {
-		str = &request.id1name;
-		retcode = idmap_str2utf8(&str, unixname, 1);
-		if (retcode != IDMAP_SUCCESS)
-			goto out;
+		request.id1name = (char *)unixname;
 		request.id1.idmap_id_u.uid = UINT32_MAX;
 	} else {
 		errno = EINVAL;
@@ -1364,20 +1521,14 @@
 	}
 	if (rid)
 		*rid = mapping->id2.idmap_id_u.sid.rid;
-	if (winname) {
-		rc = idmap_utf82str(winname, 0, &mapping->id2name);
-		if (rc != IDMAP_SUCCESS) {
-			retcode = rc;
-			goto errout;
-		}
-	}
-	if (windomain) {
-		rc = idmap_utf82str(windomain, 0, &mapping->id2domain);
-		if (rc != IDMAP_SUCCESS) {
-			retcode = rc;
-			goto errout;
-		}
-	}
+
+	rc = idmap_strdupnull(winname, mapping->id2name);
+	if (rc != IDMAP_SUCCESS)
+		retcode = rc;
+
+	rc = idmap_strdupnull(windomain, mapping->id2domain);
+	if (rc != IDMAP_SUCCESS)
+		retcode = rc;
 
 	goto out;
 
@@ -1403,94 +1554,6 @@
 }
 
 
-/*
- * utf8str to string
- */
-idmap_stat
-idmap_utf82str(char **out, size_t outsize, idmap_utf8str *in) {
-	int len;
-
-	if (in == NULL || out == NULL)
-		return (IDMAP_ERR_ARG);
-
-	if (outsize == 0) {
-		*out = NULL;
-		if ((len = in->idmap_utf8str_len) == 0)
-			return (IDMAP_SUCCESS);
-		if (in->idmap_utf8str_val == NULL)
-			return (IDMAP_ERR_ARG);
-		if (in->idmap_utf8str_val[len - 1] != '\0')
-			len++;
-		*out = calloc(1, len);
-		if (*out == NULL)
-			return (IDMAP_ERR_MEMORY);
-	} else {
-		if (*out == NULL)
-			return (IDMAP_ERR_ARG);
-		(void) memset(*out, 0, outsize);
-		if ((len = in->idmap_utf8str_len) == 0)
-			return (IDMAP_SUCCESS);
-		if (in->idmap_utf8str_val == NULL)
-			return (IDMAP_ERR_ARG);
-		if (in->idmap_utf8str_val[len - 1] != '\0')
-			len++;
-		if (outsize < len)
-			return (IDMAP_ERR_ARG);
-	}
-	(void) memcpy(*out, in->idmap_utf8str_val, in->idmap_utf8str_len);
-	return (IDMAP_SUCCESS);
-}
-
-
-/*
- * string to utf8str
- */
-idmap_stat
-idmap_str2utf8(idmap_utf8str **out, const char *in, int flag) {
-	idmap_utf8str	*tmp;
-
-	if (out == NULL)
-		return (IDMAP_ERR_ARG);
-	else if (*out == NULL) {
-		tmp = malloc(sizeof (idmap_utf8str));
-		if (tmp == NULL)
-			return (IDMAP_ERR_MEMORY);
-	} else {
-		tmp = *out;
-	}
-
-	if (in == NULL) {
-		tmp->idmap_utf8str_len = 0;
-		tmp->idmap_utf8str_val = NULL;
-		if (*out == NULL)
-			*out = tmp;
-		return (IDMAP_SUCCESS);
-	}
-
-	/* include the null terminator */
-	tmp->idmap_utf8str_len = strlen(in) + 1;
-
-	if (flag == 1) {
-		/* Don't malloc, simply assign */
-		tmp->idmap_utf8str_val = (char *)in;
-		if (*out == NULL)
-			*out = tmp;
-		return (IDMAP_SUCCESS);
-	}
-
-	tmp->idmap_utf8str_val = malloc(tmp->idmap_utf8str_len);
-	if (tmp->idmap_utf8str_val == NULL) {
-		tmp->idmap_utf8str_len = 0;
-		if (*out == NULL)
-			free(tmp);
-		return (IDMAP_ERR_MEMORY);
-	}
-	(void) memcpy(tmp->idmap_utf8str_val, in, tmp->idmap_utf8str_len);
-	if (*out == NULL)
-		*out = tmp;
-	return (IDMAP_SUCCESS);
-}
-
 
 #define	gettext(s)	s
 static stat_table_t stattable[] = {
@@ -1642,6 +1705,42 @@
 
 
 /*
+ * duplicate a string, possibly null
+ */
+static idmap_stat
+idmap_strdupnull(char **to, const char *from) {
+	if (from == NULL || *from == '\0') {
+		*to = NULL;
+		return (IDMAP_SUCCESS);
+	}
+
+	*to = strdup(from);
+	if (*to == NULL)
+		return (IDMAP_ERR_MEMORY);
+	return (IDMAP_SUCCESS);
+}
+
+idmap_stat
+idmap_namerule_cpy(idmap_namerule *to, idmap_namerule *from) {
+	idmap_stat retval;
+
+	(void) memcpy(to, from, sizeof (idmap_namerule));
+
+	retval = idmap_strdupnull(&to->windomain, from->windomain);
+	if (retval != IDMAP_SUCCESS)
+		return (retval);
+
+	retval = idmap_strdupnull(&to->winname, from->winname);
+	if (retval != IDMAP_SUCCESS)
+		return (retval);
+
+	retval = idmap_strdupnull(&to->unixname, from->unixname);
+
+	return (retval);
+}
+
+
+/*
  * Get uid given Windows name
  */
 idmap_stat
--- a/usr/src/lib/libidmap/common/idmap_impl.h	Mon Sep 17 08:01:53 2007 -0700
+++ b/usr/src/lib/libidmap/common/idmap_impl.h	Mon Sep 17 08:07:28 2007 -0700
@@ -61,11 +61,18 @@
 	struct idmap_handle	*ih;
 	idmap_update_batch	batch;
 	uint64_t		next;
+	int64_t			error_index;
+	idmap_stat		commit_stat;
+	idmap_namerule		error_rule;
+	idmap_namerule		conflict_rule;
 };
 
 #define	_IDMAP_RESET_UDT_HANDLE(uh) \
 	(void) xdr_free(xdr_idmap_update_batch, (caddr_t)&uh->batch);\
-	uh->next = 0;
+	uh->next = 0;\
+	uh->error_index = -1;\
+	(void) xdr_free(xdr_idmap_namerule, (caddr_t)&uh->error_rule);\
+	(void) xdr_free(xdr_idmap_namerule, (caddr_t)&uh->conflict_rule);
 
 typedef struct idmap_get_res {
 	idmap_id_type	idtype;
--- a/usr/src/lib/libidmap/common/idmap_priv.h	Mon Sep 17 08:01:53 2007 -0700
+++ b/usr/src/lib/libidmap/common/idmap_priv.h	Mon Sep 17 08:07:28 2007 -0700
@@ -69,6 +69,17 @@
 /* Commit */
 extern idmap_stat idmap_udt_commit(idmap_udt_handle_t *);
 
+/* Get index of the failed batch element */
+extern idmap_stat idmap_udt_get_error_index(idmap_udt_handle_t *, int64_t *);
+
+/* Get the rule which caused the batch to failed */
+extern idmap_stat idmap_udt_get_error_rule(idmap_udt_handle_t *, char **,
+    char **, char **, boolean_t *, boolean_t *, int *);
+
+/* Get the rule which caused a conflict */
+extern idmap_stat idmap_udt_get_conflict_rule(idmap_udt_handle_t *, char **,
+    char **, char **, boolean_t *, boolean_t *, int *);
+
 /* Destroy the update handle */
 extern void idmap_udt_destroy(idmap_udt_handle_t *);
 
@@ -127,18 +138,15 @@
  * Miscellaneous
  */
 
-/* utf8 to string */
-extern idmap_stat idmap_utf82str(char **, size_t, idmap_utf8str *);
-
-/* string to utf8 */
-extern idmap_stat idmap_str2utf8(idmap_utf8str **, const char *, int);
-
 /* string to status */
 extern idmap_stat idmap_string2stat(const char *);
 
 /* internal status to protocol status */
 extern idmap_stat idmap_stat4prot(idmap_stat);
 
+/* copy idmap_namerule including strings */
+extern idmap_stat idmap_namerule_cpy(idmap_namerule *, idmap_namerule *);
+
 #ifdef __cplusplus
 }
 #endif
--- a/usr/src/lib/libidmap/common/mapfile-vers	Mon Sep 17 08:01:53 2007 -0700
+++ b/usr/src/lib/libidmap/common/mapfile-vers	Mon Sep 17 08:07:28 2007 -0700
@@ -27,7 +27,6 @@
 
 SUNWprivate {
     global:
-	xdr_idmap_utf8str;
 	xdr_idmap_retcode;
 	xdr_idmap_namerule;
 	xdr_idmap_namerules_res;
@@ -39,14 +38,13 @@
 	xdr_idmap_list_namerules_1_argument;
 	xdr_idmap_mapping;
 	xdr_idmap_id_res;
+	xdr_idmap_update_res;
 	idmap_init;
 	idmap_fini;
 	idmap_free;
 	idmap_stat2string;
 	idmap_string2stat;
 	idmap_stat4prot;
-	idmap_str2utf8;
-	idmap_utf82str;
 	idmap_iter_namerules;
 	idmap_iter_next_namerule;
 	idmap_iter_mappings;
@@ -67,11 +65,15 @@
 	idmap_udt_destroy;
 	idmap_udt_commit;
 	idmap_udt_create;
+	idmap_udt_get_error_index;
+	idmap_udt_get_error_rule;
+	idmap_udt_get_conflict_rule;
 	idmap_udt_flush_namerules;
 	idmap_getwinnamebyuid;
 	idmap_getwinnamebygid;
 	idmap_getuidbywinname;
 	idmap_getgidbywinname;
+	idmap_namerule_cpy;
     local:
 	*;
 };