mkfs.xfs: add configuration file parsing support using the e2fsprogs profile parser

You may want to stick to specific set of configuration options when
creating filesystems with mkfs.xfs -- sometimes due to pure technical
reasons, but some other times to ensure systems remain compatible as
new features are introduced with older kernels, or if you always want
to take advantage of some new feature which would otherwise typically
be disruptive.

This adds support for parsing a configuration file to override defaults
parameters to be used for mkfs.xfs.

We define an XFS configuration directory, /etc/mkfs.xfs.d/ and allow for
different types of configuration files, if none is specified we look for
the default type, /etc/mkfs.xfs.d/default, and you can override with -T.
For instance, if you specify:

	mkfs.xfs -T experimental -f /dev/loop0

The file /etc/mkfs.xfs.d/experimental will be used as your configuration
file. If you really need to override the full path of the configuration
file you may use the MKFS_XFS_CONFIG environment variable.

To use /etc/ be sure to configure xfsprogs with:

 ./configure --sysconfdir=/etc/

To verify what configuration file is used on a system use the typical:

  mkfs.xfs -N

There is only a subset of options allowed to be set on the configuration
file, and currently only 1 or 0 are acceptable values. The default
parameters you can override on a configuration file and their current
built-in default settings are:

[data]
noalign=0

[inode]
align=1
projid32bit=1
sparse=0

[log]
lazy-count=1

[metadata]
crc=1
finobt=1
rmapbt=0
reflink=0

[naming]
ftype=1

[rtdev]
noalign=0

Signed-off-by: Luis R. Rodriguez <mcgrof@kernel.org>
diff --git a/configure.ac b/configure.ac
index 686bf78..b8ca4ae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -179,6 +179,7 @@
 AC_HAVE_FSTATAT
 AC_HAVE_SG_IO
 AC_HAVE_HDIO_GETGEO
+AC_HAVE_SECURE_GETENV
 AC_CONFIG_SYSTEMD_SYSTEM_UNIT_DIR
 AC_CONFIG_CROND_DIR
 
diff --git a/include/builddefs.in b/include/builddefs.in
index 7a2a626..aba3d77 100644
--- a/include/builddefs.in
+++ b/include/builddefs.in
@@ -63,6 +63,7 @@
 PKG_INC_DIR	= @includedir@/xfs
 DK_INC_DIR	= @includedir@/disk
 PKG_MAN_DIR	= @mandir@
+PKG_ETC_DIR	= @sysconfdir@
 PKG_DOC_DIR	= @datadir@/doc/@pkg_name@
 PKG_LOCALE_DIR	= @datadir@/locale
 
@@ -130,6 +131,7 @@
 HAVE_SYSTEMD = @have_systemd@
 SYSTEMD_SYSTEM_UNIT_DIR = @systemd_system_unit_dir@
 HAVE_CROND = @have_crond@
+HAVE_SECURE_GETENV = @have_secure_getenv@
 CROND_DIR = @crond_dir@
 
 GCCFLAGS = -funsigned-char -fno-strict-aliasing -Wall
@@ -172,6 +174,9 @@
 ifeq ($(HAVE_GETFSMAP),yes)
 PCFLAGS+= -DHAVE_GETFSMAP
 endif
+ifeq ($(HAVE_SECURE_GETENV),yes)
+PCFLAGS+= -DHAVE_SECURE_GETENV
+endif
 
 SANITIZER_CFLAGS += @addrsan_cflags@ @threadsan_cflags@ @ubsan_cflags@
 SANITIZER_LDFLAGS += @addrsan_ldflags@ @threadsan_ldflags@ @ubsan_ldflags@
@@ -194,6 +199,7 @@
 
 GCFLAGS = $(DEBUG) \
 	  -DVERSION=\"$(PKG_VERSION)\" -DLOCALEDIR=\"$(PKG_LOCALE_DIR)\"  \
+	  -DROOT_SYSCONFDIR=\"$(PKG_ETC_DIR)\"  \
 	  -DPACKAGE=\"$(PKG_NAME)\" -I$(TOPDIR)/include -I$(TOPDIR)/libxfs
 
 ifeq ($(ENABLE_GETTEXT),yes)
diff --git a/include/platform_defs.h.in b/include/platform_defs.h.in
index f4e4261..b3d256f 100644
--- a/include/platform_defs.h.in
+++ b/include/platform_defs.h.in
@@ -32,10 +32,25 @@
 #include <unistd.h>
 #include <pthread.h>
 #include <ctype.h>
+#include <pwd.h>
 #include <sys/types.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
 #include <limits.h>
 #include <stdbool.h>
 #include <libgen.h>
+#include <semaphore.h>
+
+#define HAVE_STDLIB_H
+#define HAVE_UNISTD_H
+#define HAVE_PWD_H
+#define HAVE_FCNTL
+#define HAVE_SYS_PRCTL_H
+#define HAVE_SYS_TYPES_H
+#define HAVE_STAT
+#define HAVE_SEMAPHORE_H
+#define HAVE_SEM_INIT /* sem_init() */
+#define HAVE_GETWUID_R /* provided by unistd.h, getuid() */
 
 typedef struct filldir		filldir_t;
 
diff --git a/m4/package_libcdev.m4 b/m4/package_libcdev.m4
index 0a6b514..870cfd7 100644
--- a/m4/package_libcdev.m4
+++ b/m4/package_libcdev.m4
@@ -275,6 +275,22 @@
   ])
 
 #
+# Check if we have the secure_getenv() call
+#
+AC_DEFUN([AC_HAVE_SECURE_GETENV],
+  [ AC_MSG_CHECKING([for secure_getenv])
+    AC_TRY_COMPILE([
+#define _GNU_SOURCE
+#include <stdlib.h>
+    ], [
+	secure_getenv(0);
+    ],	have_secure_getenv=yes
+	AC_MSG_RESULT(yes),
+	AC_MSG_RESULT(no))
+    AC_SUBST(have_secure_getenv)
+  ])
+
+#
 # Check if we need to override the system struct fsxattr with
 # the internal definition.  This /only/ happens if the system
 # actually defines struct fsxattr /and/ the system definition
diff --git a/man/man5/mkfs.xfs.d.5 b/man/man5/mkfs.xfs.d.5
new file mode 100644
index 0000000..89fd28c
--- /dev/null
+++ b/man/man5/mkfs.xfs.d.5
@@ -0,0 +1,121 @@
+.TH mkfs.xfs.d 5
+.SH NAME
+mkfs.xfs.d \- mkfs.xfs configuration directory
+.SH DESCRIPTION
+.B mkfs.xfs (8)
+uses a set of initial default parameters for configuration. These defaults
+are conservatively decided by the community as xfsprogs and features for XFS
+in the kernel advance. One can override these default on the
+.B mkfs.xfs (8)
+command line, but there are cases where it is desirable for sensible defaults
+to always be specified by the system where
+.B mkfs.xfs (8)
+runs on. This may desirable for example on systems with old kernels where the
+built-in default parameters on
+.B mkfs.xfs (8)
+may not be able to create a filesystem which the old kernel supports and it
+would be unclear what parameters are needed to produce a compatible filesystem.
+Overriding
+.B mkfs.xfs (8)
+built-in defaults may also be desirable if you have a series of systems with
+different kernels and want to be able to create filesystems which all systems
+are able to support properly.
+.PP
+The XFS configuration directory
+.B mkfs.xfs.d (5)
+can be used to define different configuration file types which can be used to
+override the built-in default parameters by
+.B mkfs.xfs (8).
+Different configuration file types are supported, the default
+configuration file type,
+.I /etc/mkfs.xfs.d/default
+, will be looked for first and if present will be used to override
+.B mkfs.xfs (8)
+built-in default parameters. You can override the configuration file type by
+specifying the type when using
+.B mkfs.xfs (8)
+by using the
+.B -T
+parameter. For example:
+.I mkfs.xfs -T experimental -f /dev/sda1
+will make
+.B mkfs.xfs (8)
+look for and use the configuration file type
+.I /etc/mkfs.xfs.d/experimental
+to override
+.B mkfs.xfs (8)
+default parameters. If you need to override the full path for a configuration
+file type you can use the
+.I MKFS_XFS_CONFIG
+environment variable prior to calling
+.B mkfs.xfs (8)
+to define the
+full path to the configuration file to be used. If you used the
+.B -T
+parameter or if you set the
+.I MKFS_XFS_CONFIG
+environment variable the configuration file must be present and should parse
+correctly.
+.PP
+Parameters passed to to the
+.B mkfs.xfs (8)
+command line always override any defaults set on the configuration file used.
+.PP
+.B mkfs.xfs (8)
+will always describe what configuration file was used, if any
+was used at all. To verify which configuration file would be used prior to
+execution of
+.B mkfs.xfs (8)
+you can use
+.I mkfs.xfs -N.
+.PP
+.SH DEFAULT PARAMETERS
+Default parameters for
+.B mkfs.xfs (8)
+consists of a small subset of the parameters one can set with on the command
+line. Default parameters can only be either enabled or disabled, you can set
+their value to 1 to enable or 0 to disable. Below we list the different
+supported default parameters which can be defined on configuration files, along
+with the current built-in setting.
+.PP
+.BI [data]
+.br
+.BI noalign=0
+.PP
+.BI [inode]
+.br
+.BI align=1
+.br
+.BI projid32bit=1
+.br
+.BI sparse=0
+.PP
+.BI [log]
+.br
+.BI lazy-count=1
+.PP
+.BI [metadata]
+.br
+.BI crc=1
+.br
+.BI finobt=1
+.br
+.BI rmapbt=0
+.br
+.BI reflink=0
+.PP
+.BI [naming]
+.br
+.BI ftype=1
+.PP
+.BI [rtdev]
+.br
+.BI noalign=0
+.PP
+.SH SEE ALSO
+.BR mkfs.xfs (8),
+.BR xfsctl (3),
+.BR xfs_info (8),
+.BR xfs_admin (8),
+.BR xfsdump (8),
+.BR xfsrestore (8).
diff --git a/man/man8/mkfs.xfs.8 b/man/man8/mkfs.xfs.8
index 4b8c78c..18489b3 100644
--- a/man/man8/mkfs.xfs.8
+++ b/man/man8/mkfs.xfs.8
@@ -83,6 +83,23 @@
 .B \-l internal \-l size=10m
 are equivalent.
 .PP
+An optional XFS configuration type file directory
+.B mkfs.xfs.d (5)
+exists to help fine tune default parameters which can be used when calling
+.B mkfs.xfs (8), by default type will be used by default, /etc/mkfs.xfs.d/default.
+Command line arguments directly passed to
+.B mkfs.xfs (8)
+will always override parameters set it the configuration type file.
+You can override configuration file type on the
+.B mkfs.xfs.d (5)
+directory by using the -t parameter and secifying the type. Alternatively
+you can set and use the MKFS_XFS_CONFIG environment variable to override
+the default full path of the first file
+.B mkfs.xfs (8)
+looks for.
+If you use -t the type configuration file must be present under
+.B mkfs.xfs.d (8).
+.PP
 In the descriptions below, sizes are given in sectors, bytes, blocks,
 kilobytes, megabytes, gigabytes, etc.
 Sizes are treated as hexadecimal if prefixed by 0x or 0X,
@@ -123,6 +140,11 @@
 disable or enable the functionality.
 .SH OPTIONS
 .TP
+.BI \-t " configuration-type"
+Override the default type of the configuratio file under
+.B mkfs.xfs.d
+used.
+.TP
 .BI \-b " block_size_options"
 This option specifies the fundamental block size of the filesystem.
 The valid
@@ -923,6 +945,7 @@
 .SH SEE ALSO
 .BR xfs (5),
 .BR mkfs (8),
+.BR mkfs.xfs.d (5),
 .BR mount (8),
 .BR xfs_info (8),
 .BR xfs_admin (8).
diff --git a/mkfs/Makefile b/mkfs/Makefile
index c84f9b6..a4cec04 100644
--- a/mkfs/Makefile
+++ b/mkfs/Makefile
@@ -8,8 +8,10 @@
 LTCOMMAND = mkfs.xfs
 
 HFILES =
-CFILES = proto.c xfs_mkfs.c
+PROFILE_C := profile.c prof_err.c error_message.c et_name.c profile_helpers.c
+CFILES = proto.c xfs_mkfs.c $(PROFILE_C)
 
+LCFLAGS += -include platform_defs.h
 LLDLIBS += $(LIBXFS) $(LIBXCMD) $(LIBFROG) $(LIBRT) $(LIBPTHREAD) $(LIBBLKID) \
 	$(LIBUUID)
 LTDEPENDENCIES += $(LIBXFS) $(LIBXCMD) $(LIBFROG)
diff --git a/mkfs/error_message.c b/mkfs/error_message.c
new file mode 100644
index 0000000..c6974a6
--- /dev/null
+++ b/mkfs/error_message.c
@@ -0,0 +1,347 @@
+/*
+ * $Header$
+ * $Source$
+ * $Locker$
+ *
+ * Copyright 1987 by the Student Information Processing Board
+ * of the Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose is hereby granted, provided that
+ * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  M.I.T. and the
+ * M.I.T. S.I.P.B. make no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without
+ * express or implied warranty.
+ */
+
+#ifdef PROFILE_USE_CONFIG
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#else
+#define PR_GET_DUMPABLE 3
+#endif
+#if (!defined(HAVE_PRCTL) && defined(linux))
+#include <sys/syscall.h>
+#endif
+#ifdef HAVE_SEMAPHORE_H
+#include <semaphore.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include "com_err.h"
+#include "error_table.h"
+#include "internal.h"
+
+#ifdef TLS
+#define THREAD_LOCAL static TLS
+#else
+#define THREAD_LOCAL static
+#endif
+
+THREAD_LOCAL char buffer[25];
+
+struct et_list * _et_list = (struct et_list *) NULL;
+struct et_list * _et_dynamic_list = (struct et_list *) NULL;
+
+#ifdef __GNUC__
+#define COMERR_ATTR(x) __attribute__(x)
+#else
+#define COMERR_ATTR(x)
+#endif
+
+#ifdef HAVE_SEM_INIT
+static sem_t _et_lock;
+static int _et_lock_initialized;
+
+static void COMERR_ATTR((constructor)) setup_et_lock(void)
+{
+	sem_init(&_et_lock, 0, 1);
+	_et_lock_initialized = 1;
+}
+
+static void COMERR_ATTR((destructor)) fini_et_lock(void)
+{
+	sem_destroy(&_et_lock);
+	_et_lock_initialized = 0;
+}
+#endif
+
+
+int et_list_lock(void)
+{
+#ifdef HAVE_SEM_INIT
+	if (!_et_lock_initialized)
+		setup_et_lock();
+	return sem_wait(&_et_lock);
+#else
+	return 0;
+#endif
+}
+
+int et_list_unlock(void)
+{
+#ifdef HAVE_SEM_INIT
+	if (_et_lock_initialized)
+		return sem_post(&_et_lock);
+#endif
+	return 0;
+}
+
+typedef char *(*gettextf) (const char *);
+
+static gettextf com_err_gettext = NULL;
+
+gettextf set_com_err_gettext(gettextf new_proc)
+{
+    gettextf x = com_err_gettext;
+
+    com_err_gettext = new_proc;
+
+    return x;
+}
+
+
+const char * error_message (errcode_t code)
+{
+    int offset;
+    struct et_list *et;
+    errcode_t table_num;
+    int started = 0;
+    char *cp;
+
+    offset = (int) (code & ((1<<ERRCODE_RANGE)-1));
+    table_num = code - offset;
+    if (!table_num) {
+#ifdef HAS_SYS_ERRLIST
+	if (offset < sys_nerr)
+	    return(sys_errlist[offset]);
+	else
+	    goto oops;
+#else
+	cp = strerror(offset);
+	if (cp)
+	    return(cp);
+	else
+	    goto oops;
+#endif
+    }
+    et_list_lock();
+    for (et = _et_list; et; et = et->next) {
+	if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) {
+	    /* This is the right table */
+	    if (et->table->n_msgs <= offset) {
+		break;
+	    } else {
+		const char *msg = et->table->msgs[offset];
+		et_list_unlock();
+		if (com_err_gettext)
+		    return (*com_err_gettext)(msg);
+		else
+		    return msg;
+	    }
+	}
+    }
+    for (et = _et_dynamic_list; et; et = et->next) {
+	if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) {
+	    /* This is the right table */
+	    if (et->table->n_msgs <= offset) {
+		break;
+	    } else {
+		const char *msg = et->table->msgs[offset];
+		et_list_unlock();
+		if (com_err_gettext)
+		    return (*com_err_gettext)(msg);
+		else
+		    return msg;
+	    }
+	}
+    }
+    et_list_unlock();
+oops:
+    strcpy (buffer, "Unknown code ");
+    if (table_num) {
+	strcat (buffer, error_table_name (table_num));
+	strcat (buffer, " ");
+    }
+    for (cp = buffer; *cp; cp++)
+	;
+    if (offset >= 100) {
+	*cp++ = '0' + offset / 100;
+	offset %= 100;
+	started++;
+    }
+    if (started || offset >= 10) {
+	*cp++ = '0' + offset / 10;
+	offset %= 10;
+    }
+    *cp++ = '0' + offset;
+    *cp = '\0';
+    return(buffer);
+}
+
+/*
+ * This routine will only return a value if the we are not running as
+ * a privileged process.
+ */
+static char *safe_getenv(const char *arg)
+{
+#if !defined(_WIN32)
+	if ((getuid() != geteuid()) || (getgid() != getegid()))
+		return NULL;
+#endif
+#if HAVE_PRCTL
+	if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
+		return NULL;
+#else
+#if (defined(linux) && defined(SYS_prctl))
+	if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
+		return NULL;
+#endif
+#endif
+
+#if defined(HAVE_SECURE_GETENV)
+	return secure_getenv(arg);
+#elif defined(HAVE___SECURE_GETENV)
+	return __secure_getenv(arg);
+#else
+	return getenv(arg);
+#endif
+}
+
+#define DEBUG_INIT	0x8000
+#define DEBUG_ADDREMOVE 0x0001
+
+static int debug_mask = 0;
+static FILE *debug_f = 0;
+
+static void init_debug(void)
+{
+	char	*dstr, *fn, *tmp;
+#if defined(HAVE_FCNTL)
+	int	fd, flags;
+#endif
+
+	if (debug_mask & DEBUG_INIT)
+		return;
+
+	dstr = getenv("COMERR_DEBUG");
+	if (dstr) {
+		debug_mask = strtoul(dstr, &tmp, 0);
+		if (*tmp || errno)
+			debug_mask = 0;
+	}
+
+	debug_mask |= DEBUG_INIT;
+	if (debug_mask == DEBUG_INIT)
+		return;
+
+	fn = safe_getenv("COMERR_DEBUG_FILE");
+	if (fn)
+		debug_f = fopen(fn, "a");
+	if (!debug_f)
+		debug_f = fopen("/dev/tty", "a");
+	if (debug_f) {
+#if defined(HAVE_FCNTL)
+		fd = fileno(debug_f);
+		if (fd >= 0) {
+			flags = fcntl(fd, F_GETFD);
+			if (flags >= 0)
+				fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
+		}
+#endif
+	} else
+		debug_mask = DEBUG_INIT;
+
+}
+
+/*
+ * New interface provided by krb5's com_err library
+ */
+errcode_t add_error_table(const struct error_table * et)
+{
+	struct et_list *el;
+
+	if (!(el = (struct et_list *) malloc(sizeof(struct et_list))))
+		return ENOMEM;
+
+	if (et_list_lock() != 0) {
+		free(el);
+		return errno;
+	}
+
+	el->table = et;
+	el->next = _et_dynamic_list;
+	_et_dynamic_list = el;
+
+	init_debug();
+	if (debug_mask & DEBUG_ADDREMOVE)
+		fprintf(debug_f, "add_error_table: %s (0x%p)\n",
+			error_table_name(et->base),
+			(const void *) et);
+
+	et_list_unlock();
+	return 0;
+}
+
+/*
+ * New interface provided by krb5's com_err library
+ */
+errcode_t remove_error_table(const struct error_table * et)
+{
+	struct et_list *el;
+	struct et_list *el2 = 0;
+
+	if (et_list_lock() != 0)
+		return ENOENT;
+
+	el = _et_dynamic_list;
+	init_debug();
+	while (el) {
+		if (el->table->base == et->base) {
+			if (el2)	/* Not the beginning of the list */
+				el2->next = el->next;
+			else
+				_et_dynamic_list = el->next;
+			(void) free(el);
+			if (debug_mask & DEBUG_ADDREMOVE)
+				fprintf(debug_f,
+					"remove_error_table: %s (0x%p)\n",
+					error_table_name(et->base),
+					(const void *) et);
+			et_list_unlock();
+			return 0;
+		}
+		el2 = el;
+		el = el->next;
+	}
+	if (debug_mask & DEBUG_ADDREMOVE)
+		fprintf(debug_f, "remove_error_table FAILED: %s (0x%p)\n",
+			error_table_name(et->base),
+			(const void *) et);
+	et_list_unlock();
+	return ENOENT;
+}
+
+/*
+ * Variant of the interface provided by Heimdal's com_err library
+ */
+void
+add_to_error_table(struct et_list *new_table)
+{
+	add_error_table(new_table->table);
+}
diff --git a/mkfs/error_table.h b/mkfs/error_table.h
new file mode 100644
index 0000000..24e4762
--- /dev/null
+++ b/mkfs/error_table.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1988 by the Student Information Processing Board of the
+ * Massachusetts Institute of Technology.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose is hereby granted, provided that
+ * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  M.I.T. and the
+ * M.I.T. S.I.P.B. make no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without
+ * express or implied warranty.
+ */
+
+#ifndef _ET_H
+
+struct et_list {
+    struct et_list *next;
+    const struct error_table *table;
+};
+extern struct et_list *_et_list, *_et_dynamic_list;
+
+#define	ERRCODE_RANGE	8	/* # of bits to shift table number */
+#define	BITS_PER_CHAR	6	/* # bits to shift per character in name */
+
+extern const char *error_table_name(errcode_t num);
+
+#define _ET_H
+#endif
diff --git a/mkfs/et_name.c b/mkfs/et_name.c
new file mode 100644
index 0000000..085176b
--- /dev/null
+++ b/mkfs/et_name.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright 1987 by MIT Student Information Processing Board
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose is hereby granted, provided that
+ * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  M.I.T. and the
+ * M.I.T. S.I.P.B. make no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without
+ * express or implied warranty.
+ */
+
+#ifdef PROFILE_USE_CONFIG
+#include "config.h"
+#endif
+#include "com_err.h"
+#include "error_table.h"
+#include "internal.h"
+
+static const char char_set[] =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
+
+static char buf[6];
+
+const char * error_table_name(errcode_t num)
+{
+    int ch;
+    int i;
+    char *p;
+
+    /* num = aa aaa abb bbb bcc ccc cdd ddd d?? ??? ??? */
+    p = buf;
+    num >>= ERRCODE_RANGE;
+    /* num = ?? ??? ??? aaa aaa bbb bbb ccc ccc ddd ddd */
+    num &= 077777777L;
+    /* num = 00 000 000 aaa aaa bbb bbb ccc ccc ddd ddd */
+    for (i = 4; i >= 0; i--) {
+	ch = (int)((num >> BITS_PER_CHAR * i) & ((1 << BITS_PER_CHAR) - 1));
+	if (ch != 0)
+	    *p++ = char_set[ch-1];
+    }
+    *p = '\0';
+    return(buf);
+}
diff --git a/mkfs/internal.h b/mkfs/internal.h
new file mode 100644
index 0000000..d16f373
--- /dev/null
+++ b/mkfs/internal.h
@@ -0,0 +1,19 @@
+/*
+ * internal include file for com_err package
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose is hereby granted, provided that
+ * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  M.I.T. and the
+ * M.I.T. S.I.P.B. make no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without
+ * express or implied warranty.
+ */
+
+#include <errno.h>
+
+#ifdef NEED_SYS_ERRLIST
+extern char const * const sys_errlist[];
+extern const int sys_nerr;
+#endif
diff --git a/mkfs/prof_err.c b/mkfs/prof_err.c
new file mode 100644
index 0000000..da5bf85
--- /dev/null
+++ b/mkfs/prof_err.c
@@ -0,0 +1,87 @@
+/*
+ * prof_err.c:
+ * This file is automatically generated; please do not edit it.
+ */
+
+#include <stdlib.h>
+
+#ifndef N_
+#define N_(a) a
+#endif
+
+static const char * const text[] = {
+	N_("Profile version 0.0"),
+	N_("Bad magic value in profile_node"),
+	N_("Profile section not found"),
+	N_("Profile relation not found"),
+	N_(	"Attempt to add a relation to node which is not a section"),
+	N_(	"A profile section header has a non-zero value"),
+	N_("Bad linked list in profile structures"),
+	N_("Bad group level in profile structures"),
+	N_(	"Bad parent pointer in profile structures"),
+	N_("Bad magic value in profile iterator"),
+	N_("Can't set value on section node"),
+	N_("Invalid argument passed to profile library"),
+	N_("Attempt to modify read-only profile"),
+	N_("Profile section header not at top level"),
+	N_("Syntax error in profile section header"),
+	N_("Syntax error in profile relation"),
+	N_("Extra closing brace in profile"),
+	N_("Missing open brace in profile"),
+	N_("Bad magic value in profile_t"),
+	N_("Bad magic value in profile_section_t"),
+	N_(	"Iteration through all top level section not supported"),
+	N_("Invalid profile_section object"),
+	N_("No more sections"),
+	N_("Bad nameset passed to query routine"),
+	N_("No profile file open"),
+	N_("Bad magic value in profile_file_t"),
+	N_("Couldn't open profile file"),
+	N_("Section already exists"),
+	N_("Invalid boolean value"),
+	N_("Invalid integer value"),
+	N_("Bad magic value in profile_file_data_t"),
+    0
+};
+
+struct error_table {
+    char const * const * msgs;
+    long base;
+    int n_msgs;
+};
+struct et_list {
+    struct et_list *next;
+    const struct error_table * table;
+};
+extern struct et_list *_et_list;
+
+const struct error_table et_prof_error_table = { text, -1429577728L, 31 };
+
+static struct et_list __et_link = { 0, 0 };
+
+void initialize_prof_error_table_r(struct et_list **list);
+void initialize_prof_error_table(void);
+
+void initialize_prof_error_table(void) {
+    initialize_prof_error_table_r(&_et_list);
+}
+
+/* For Heimdal compatibility */
+void initialize_prof_error_table_r(struct et_list **list)
+{
+    struct et_list *et, **end;
+
+    for (end = list, et = *list; et; end = &et->next, et = et->next)
+        if (et->table->msgs == text)
+            return;
+    et = malloc(sizeof(struct et_list));
+    if (et == 0) {
+        if (!__et_link.table)
+            et = &__et_link;
+        else
+            return;
+    }
+    et->table = &et_prof_error_table;
+    et->next = 0;
+    *end = et;
+}
diff --git a/mkfs/prof_err.h b/mkfs/prof_err.h
new file mode 100644
index 0000000..e8066dd
--- /dev/null
+++ b/mkfs/prof_err.h
@@ -0,0 +1,49 @@
+/*
+ * prof_err.h:
+ * This file is automatically generated; please do not edit it.
+ */
+
+#include <et/com_err.h>
+
+#define PROF_VERSION                             (-1429577728L)
+#define PROF_MAGIC_NODE                          (-1429577727L)
+#define PROF_NO_SECTION                          (-1429577726L)
+#define PROF_NO_RELATION                         (-1429577725L)
+#define PROF_ADD_NOT_SECTION                     (-1429577724L)
+#define PROF_SECTION_WITH_VALUE                  (-1429577723L)
+#define PROF_BAD_LINK_LIST                       (-1429577722L)
+#define PROF_BAD_GROUP_LVL                       (-1429577721L)
+#define PROF_BAD_PARENT_PTR                      (-1429577720L)
+#define PROF_MAGIC_ITERATOR                      (-1429577719L)
+#define PROF_SET_SECTION_VALUE                   (-1429577718L)
+#define PROF_EINVAL                              (-1429577717L)
+#define PROF_READ_ONLY                           (-1429577716L)
+#define PROF_SECTION_NOTOP                       (-1429577715L)
+#define PROF_SECTION_SYNTAX                      (-1429577714L)
+#define PROF_RELATION_SYNTAX                     (-1429577713L)
+#define PROF_EXTRA_CBRACE                        (-1429577712L)
+#define PROF_MISSING_OBRACE                      (-1429577711L)
+#define PROF_MAGIC_PROFILE                       (-1429577710L)
+#define PROF_MAGIC_SECTION                       (-1429577709L)
+#define PROF_TOPSECTION_ITER_NOSUPP              (-1429577708L)
+#define PROF_INVALID_SECTION                     (-1429577707L)
+#define PROF_END_OF_SECTIONS                     (-1429577706L)
+#define PROF_BAD_NAMESET                         (-1429577705L)
+#define PROF_NO_PROFILE                          (-1429577704L)
+#define PROF_MAGIC_FILE                          (-1429577703L)
+#define PROF_FAIL_OPEN                           (-1429577702L)
+#define PROF_EXISTS                              (-1429577701L)
+#define PROF_BAD_BOOLEAN                         (-1429577700L)
+#define PROF_BAD_INTEGER                         (-1429577699L)
+#define PROF_MAGIC_FILE_DATA                     (-1429577698L)
+extern const struct error_table et_prof_error_table;
+extern void initialize_prof_error_table(void);
+
+/* For compatibility with Heimdal */
+extern void initialize_prof_error_table_r(struct et_list **list);
+
+#define ERROR_TABLE_BASE_prof (-1429577728L)
+
+/* for compatibility with older versions... */
+#define init_prof_err_tbl initialize_prof_error_table
+#define prof_err_base ERROR_TABLE_BASE_prof
diff --git a/mkfs/profile.c b/mkfs/profile.c
new file mode 100644
index 0000000..f7c1f3c
--- /dev/null
+++ b/mkfs/profile.c
@@ -0,0 +1,1929 @@
+/*
+ * profile.c -- A simple configuration file parsing "library in a file"
+ *
+ * The profile library was originally written by Theodore Ts'o in 1995
+ * for use in the MIT Kerberos v5 library.  It has been
+ * modified/enhanced/bug-fixed over time by other members of the MIT
+ * Kerberos team.  This version was originally taken from the Kerberos
+ * v5 distribution, version 1.4.2, and radically simplified for use in
+ * e2fsprogs.  (Support for locking for multi-threaded operations,
+ * being able to modify and update the configuration file
+ * programmatically, and Mac/Windows portability have been removed.
+ * It has been folded into a single C source file to make it easier to
+ * fold into an application program.)
+ *
+ * Copyright (C) 2005, 2006 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ * Copyright (C) 1985-2005 by the Massachusetts Institute of Technology.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government.  It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original MIT software.
+ * M.I.T. makes no representations about the suitability of this software
+ * for any purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifdef PROFILE_USE_CONFIG
+#include "config.h"
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <time.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#include <et/com_err.h>
+#include "profile.h"
+#include "prof_err.h"
+
+#undef STAT_ONCE_PER_SECOND
+#undef HAVE_STAT
+
+/*
+ * prof_int.h
+ */
+
+typedef long prf_magic_t;
+
+/*
+ * This is the structure which stores the profile information for a
+ * particular configuration file.
+ */
+struct _prf_file_t {
+	prf_magic_t	magic;
+	char		*filespec;
+#ifdef STAT_ONCE_PER_SECOND
+	time_t		last_stat;
+#endif
+	time_t		timestamp; /* time tree was last updated from file */
+	int		flags;	/* r/w, dirty */
+	int		upd_serial; /* incremented when data changes */
+	struct profile_node *root;
+	struct _prf_file_t *next;
+};
+
+typedef struct _prf_file_t *prf_file_t;
+
+/*
+ * The profile flags
+ */
+#define PROFILE_FILE_RW		0x0001
+#define PROFILE_FILE_DIRTY	0x0002
+#define PROFILE_FILE_NO_RELOAD	0x0004
+
+/*
+ * This structure defines the high-level, user visible profile_t
+ * object, which is used as a handle by users who need to query some
+ * configuration file(s)
+ */
+struct _profile_t {
+	prf_magic_t	magic;
+	prf_file_t	first_file;
+	struct stat first_st;
+};
+
+/*
+ * Used by the profile iterator in prof_get.c
+ */
+#define PROFILE_ITER_LIST_SECTION	0x0001
+#define PROFILE_ITER_SECTIONS_ONLY	0x0002
+#define PROFILE_ITER_RELATIONS_ONLY	0x0004
+
+#define PROFILE_ITER_FINAL_SEEN		0x0100
+
+/*
+ * Check if a filespec is last in a list (NULL on UNIX, invalid FSSpec on MacOS
+ */
+
+#define	PROFILE_LAST_FILESPEC(x) (((x) == NULL) || ((x)[0] == '\0'))
+
+struct profile_node {
+	errcode_t	magic;
+	char *name;
+	char *value;
+	int group_level;
+	unsigned int final:1;		/* Indicate don't search next file */
+	unsigned int deleted:1;
+	struct profile_node *first_child;
+	struct profile_node *parent;
+	struct profile_node *next, *prev;
+};
+
+#define CHECK_MAGIC(node) \
+	  if ((node)->magic != PROF_MAGIC_NODE) \
+		  return PROF_MAGIC_NODE;
+
+/* profile parser declarations */
+struct parse_state {
+	int	state;
+	int	group_level;
+	int	line_num;
+	struct profile_node *root_section;
+	struct profile_node *current_section;
+};
+
+static const char *default_filename = "<default>";
+
+static profile_syntax_err_cb_t	syntax_err_cb;
+
+static errcode_t parse_line(char *line, struct parse_state *state);
+
+#ifdef DEBUG_PROGRAM
+static errcode_t profile_write_tree_file
+	(struct profile_node *root, FILE *dstfile);
+
+static errcode_t profile_write_tree_to_buffer
+	(struct profile_node *root, char **buf);
+#endif
+
+
+static void profile_free_node
+	(struct profile_node *relation);
+
+static errcode_t profile_create_node
+	(const char *name, const char *value,
+		   struct profile_node **ret_node);
+
+#ifdef DEBUG_PROGRAM
+static errcode_t profile_verify_node
+	(struct profile_node *node);
+#endif
+
+static errcode_t profile_add_node
+	(struct profile_node *section,
+		    const char *name, const char *value,
+		    struct profile_node **ret_node);
+
+static errcode_t profile_find_node
+	(struct profile_node *section,
+		    const char *name, const char *value,
+		    int section_flag, void **state,
+		    struct profile_node **node);
+
+static errcode_t profile_node_iterator
+	(void	**iter_p, struct profile_node **ret_node,
+		   char **ret_name, char **ret_value);
+
+static errcode_t profile_open_file
+	(const char * file, prf_file_t *ret_prof);
+
+static errcode_t profile_update_file
+	(prf_file_t prf);
+
+static void profile_free_file
+	(prf_file_t profile);
+
+static errcode_t profile_get_value(profile_t profile, const char *name,
+				   const char *subname, const char *subsubname,
+				   const char **ret_value);
+
+
+/*
+ * prof_init.c --- routines that manipulate the user-visible profile_t
+ * 	object.
+ */
+
+static int compstr(const void *m1, const void *m2)
+{
+	const char *s1 = *((const char * const *) m1);
+	const char *s2 = *((const char * const *) m2);
+
+	return strcmp(s1, s2);
+}
+
+static void free_list(char **list)
+{
+    char	**cp;
+
+    if (list == 0)
+	    return;
+
+    for (cp = list; *cp; cp++)
+	free(*cp);
+    free(list);
+}
+
+static errcode_t get_dirlist(const char *dirname, char***ret_array)
+{
+	DIR *dir;
+	struct dirent *de;
+	struct stat st;
+	errcode_t retval;
+	char *fn, *cp;
+	char **array = 0, **new_array;
+	int max = 0, num = 0;
+
+	dir = opendir(dirname);
+	if (!dir)
+		return errno;
+
+	while ((de = readdir(dir)) != NULL) {
+		for (cp = de->d_name; *cp; cp++) {
+			if (!isalnum(*cp) &&
+			    (*cp != '-') &&
+			    (*cp != '_'))
+				break;
+		}
+		if (*cp)
+			continue;
+		fn = malloc(strlen(dirname) + strlen(de->d_name) + 2);
+		if (!fn) {
+			retval = ENOMEM;
+			goto errout;
+		}
+		sprintf(fn, "%s/%s", dirname, de->d_name);
+		if ((stat(fn, &st) < 0) || !S_ISREG(st.st_mode)) {
+			free(fn);
+			continue;
+		}
+		if (num >= max) {
+			max += 10;
+			new_array = realloc(array, sizeof(char *) * (max+1));
+			if (!new_array) {
+				retval = ENOMEM;
+				free(fn);
+				goto errout;
+			}
+			array = new_array;
+		}
+		array[num++] = fn;
+	}
+	if (array) {
+		qsort(array, num, sizeof(char *), compstr);
+		array[num++] = 0;
+	}
+	*ret_array = array;
+	closedir(dir);
+	return 0;
+errout:
+	if (array)
+		array[num] = 0;
+	closedir(dir);
+	free_list(array);
+	return retval;
+}
+
+errcode_t
+profile_init(const char * const *files, profile_t *ret_profile)
+{
+	const char * const *fs;
+	profile_t profile;
+	prf_file_t  new_file, *last;
+	errcode_t retval = 0;
+	char **cpp, *cp, **array = 0;
+
+	profile = malloc(sizeof(struct _profile_t));
+	if (!profile)
+		return ENOMEM;
+	memset(profile, 0, sizeof(struct _profile_t));
+	profile->magic = PROF_MAGIC_PROFILE;
+	last = &profile->first_file;
+
+        /* if the filenames list is not specified return an empty profile */
+        if ( files ) {
+	    for (fs = files; !PROFILE_LAST_FILESPEC(*fs); fs++) {
+		if (array)
+			free_list(array);
+		array = NULL;
+		retval = get_dirlist(*fs, &array);
+		if (retval == 0) {
+			if (!array)
+				continue;
+			for (cpp = array; (cp = *cpp); cpp++) {
+				retval = profile_open_file(cp, &new_file);
+				if (retval == EACCES)
+					continue;
+				if (retval)
+					goto errout;
+				*last = new_file;
+				last = &new_file->next;
+			}
+		} else if ((retval != ENOTDIR) &&
+			   strcmp(*fs, default_filename))
+			goto errout;
+
+		retval = profile_open_file(*fs, &new_file);
+		/* if this file is missing, skip to the next */
+		if (retval == ENOENT || retval == EACCES) {
+			continue;
+		}
+		if (retval)
+			goto errout;
+		*last = new_file;
+		last = &new_file->next;
+	    }
+	    /*
+	     * If all the files were not found, return the appropriate error.
+	     */
+	    if (!profile->first_file) {
+		retval = ENOENT;
+		goto errout;
+	    }
+	}
+
+	free_list(array);
+        *ret_profile = profile;
+        return 0;
+errout:
+	free_list(array);
+	profile_release(profile);
+	return retval;
+}
+
+void
+profile_release(profile_t profile)
+{
+	prf_file_t	p, next;
+
+	if (!profile || profile->magic != PROF_MAGIC_PROFILE)
+		return;
+
+	for (p = profile->first_file; p; p = next) {
+		next = p->next;
+		profile_free_file(p);
+	}
+	profile->magic = 0;
+	free(profile);
+}
+
+/*
+ * This function sets the value of the pseudo file "<default>".  If
+ * the file "<default>" had previously been passed to profile_init(),
+ * then def_string parameter will be parsed and used as the profile
+ * information for the "<default>" file.
+ */
+errcode_t profile_set_default(profile_t profile, const char *def_string)
+{
+	struct parse_state	state;
+	prf_file_t		prf;
+	errcode_t		retval;
+	const char		*in;
+	char			*line, *p, *end;
+	int			line_size, len;
+
+	if (!def_string || !profile || profile->magic != PROF_MAGIC_PROFILE)
+		return PROF_MAGIC_PROFILE;
+
+	for (prf = profile->first_file; prf; prf = prf->next) {
+		if (strcmp(prf->filespec, default_filename) == 0)
+			break;
+	}
+	if (!prf)
+		return 0;
+
+	if (prf->root) {
+		profile_free_node(prf->root);
+		prf->root = 0;
+	}
+
+	memset(&state, 0, sizeof(struct parse_state));
+	retval = profile_create_node("(root)", 0, &state.root_section);
+	if (retval)
+		return retval;
+
+	line = 0;
+	line_size = 0;
+	in = def_string;
+	while (*in) {
+		end = strchr(in, '\n');
+		len = end ? (end - in) : (int) strlen(in);
+		if (len >= line_size) {
+			line_size = len+1;
+			p = realloc(line, line_size);
+			if (!p) {
+				retval = ENOMEM;
+				goto errout;
+			}
+			line = p;
+		}
+		memcpy(line, in, len);
+		line[len] = 0;
+		retval = parse_line(line, &state);
+		if (retval) {
+		errout:
+			if (syntax_err_cb)
+				(syntax_err_cb)(prf->filespec, retval,
+						state.line_num);
+			free(line);
+			if (prf->root)
+				profile_free_node(prf->root);
+			return retval;
+		}
+		if (!end)
+			break;
+		in = end+1;
+	}
+	prf->root = state.root_section;
+	free(line);
+
+	return 0;
+}
+
+/*
+ * prof_file.c ---- routines that manipulate an individual profile file.
+ */
+
+errcode_t profile_open_file(const char * filespec,
+			    prf_file_t *ret_prof)
+{
+	prf_file_t	prf;
+	errcode_t	retval;
+	char		*home_env = 0;
+	unsigned int	len;
+	char		*expanded_filename;
+
+	prf = malloc(sizeof(struct _prf_file_t));
+	if (!prf)
+		return ENOMEM;
+	memset(prf, 0, sizeof(struct _prf_file_t));
+	prf->magic = PROF_MAGIC_FILE;
+
+	len = strlen(filespec)+1;
+	if (filespec[0] == '~' && filespec[1] == '/') {
+		home_env = getenv("HOME");
+#ifdef HAVE_PWD_H
+		if (home_env == NULL) {
+#ifdef HAVE_GETWUID_R
+		    struct passwd *pw, pwx;
+		    uid_t uid;
+		    char pwbuf[BUFSIZ];
+
+		    uid = getuid();
+		    if (!getpwuid_r(uid, &pwx, pwbuf, sizeof(pwbuf), &pw)
+			&& pw != NULL && pw->pw_dir[0] != 0)
+			home_env = pw->pw_dir;
+#else
+		    struct passwd *pw;
+
+		    pw = getpwuid(getuid());
+		    home_env = pw->pw_dir;
+#endif
+		}
+#endif
+		if (home_env)
+			len += strlen(home_env);
+	}
+	expanded_filename = malloc(len);
+	if (expanded_filename == 0) {
+	    profile_free_file(prf);
+	    return errno;
+	}
+	if (home_env) {
+	    strcpy(expanded_filename, home_env);
+	    strcat(expanded_filename, filespec+1);
+	} else
+	    memcpy(expanded_filename, filespec, len);
+
+	prf->filespec = expanded_filename;
+
+	if (strcmp(prf->filespec, default_filename) != 0) {
+		retval = profile_update_file(prf);
+		if (retval) {
+			profile_free_file(prf);
+			return retval;
+		}
+	}
+
+	*ret_prof = prf;
+	return 0;
+}
+
+errcode_t profile_update_file(prf_file_t prf)
+{
+	errcode_t retval;
+#ifdef HAVE_STAT
+	struct stat st;
+#ifdef STAT_ONCE_PER_SECOND
+	time_t now;
+#endif
+#endif
+	FILE *f;
+	char buf[2048];
+	struct parse_state state;
+
+	if (prf->flags & PROFILE_FILE_NO_RELOAD)
+		return 0;
+
+#ifdef HAVE_STAT
+#ifdef STAT_ONCE_PER_SECOND
+	now = time(0);
+	if (now == prf->last_stat && prf->root != NULL) {
+	    return 0;
+	}
+#endif
+	if (stat(prf->filespec, &st)) {
+	    retval = errno;
+	    return retval;
+	}
+#ifdef STAT_ONCE_PER_SECOND
+	prf->last_stat = now;
+#endif
+	if (st.st_mtime == prf->timestamp && prf->root != NULL) {
+	    return 0;
+	}
+	if (prf->root) {
+		profile_free_node(prf->root);
+		prf->root = 0;
+	}
+#else
+	/*
+	 * If we don't have the stat() call, assume that our in-core
+	 * memory image is correct.  That is, we won't reread the
+	 * profile file if it changes.
+	 */
+	if (prf->root) {
+	    return 0;
+	}
+#endif
+	memset(&state, 0, sizeof(struct parse_state));
+	retval = profile_create_node("(root)", 0, &state.root_section);
+	if (retval)
+		return retval;
+	errno = 0;
+	f = fopen(prf->filespec, "r");
+	if (f == NULL) {
+		retval = errno;
+		if (retval == 0)
+			retval = ENOENT;
+		return retval;
+	}
+	prf->upd_serial++;
+	while (!feof(f)) {
+		if (fgets(buf, sizeof(buf), f) == NULL)
+			break;
+		retval = parse_line(buf, &state);
+		if (retval) {
+			if (syntax_err_cb)
+				(syntax_err_cb)(prf->filespec, retval,
+						state.line_num);
+			fclose(f);
+			return retval;
+		}
+	}
+	prf->root = state.root_section;
+
+	fclose(f);
+
+#ifdef HAVE_STAT
+	prf->timestamp = st.st_mtime;
+#endif
+	return 0;
+}
+
+errcode_t profile_get_stat(profile_t profile, struct stat *st)
+{
+	errcode_t retval = 0;
+	prf_file_t prf;
+
+	if (!profile || !profile->first_file)
+		return ENOENT;
+
+	prf = profile->first_file;
+
+	if (stat(prf->filespec, &profile->first_st)) {
+	    retval = errno;
+	    return retval;
+	}
+
+	memcpy(st, &profile->first_st, sizeof(struct stat));
+
+	return retval;
+}
+
+void profile_free_file(prf_file_t prf)
+{
+    if (prf->root)
+	profile_free_node(prf->root);
+    free(prf->filespec);
+    free(prf);
+}
+
+/* Begin the profile parser */
+
+profile_syntax_err_cb_t profile_set_syntax_err_cb(profile_syntax_err_cb_t hook)
+{
+	profile_syntax_err_cb_t	old;
+
+	old = syntax_err_cb;
+	syntax_err_cb = hook;
+	return(old);
+}
+
+#define STATE_INIT_COMMENT	0
+#define STATE_STD_LINE		1
+#define STATE_GET_OBRACE	2
+
+static char *skip_over_blanks(char *cp)
+{
+	while (*cp && isspace((int) (*cp)))
+		cp++;
+	return cp;
+}
+
+static int end_or_comment(char ch)
+{
+	return (ch == 0 || ch == '#' || ch == ';');
+}
+
+static char *skip_over_nonblanks(char *cp)
+{
+	while (!end_or_comment(*cp) && !isspace(*cp))
+		cp++;
+	return cp;
+}
+
+static void strip_line(char *line)
+{
+	char *p = line + strlen(line);
+	while (p > line && (p[-1] == '\n' || p[-1] == '\r'))
+	    *p-- = 0;
+}
+
+static void parse_quoted_string(char *str)
+{
+	char *to, *from;
+
+	to = from = str;
+
+	for (to = from = str; *from && *from != '"'; to++, from++) {
+		if (*from == '\\') {
+			from++;
+			switch (*from) {
+			case 'n':
+				*to = '\n';
+				break;
+			case 't':
+				*to = '\t';
+				break;
+			case 'b':
+				*to = '\b';
+				break;
+			default:
+				*to = *from;
+			}
+			continue;
+		}
+		*to = *from;
+	}
+	*to = '\0';
+}
+
+static errcode_t parse_line(char *line, struct parse_state *state)
+{
+	char	*cp, ch, *tag, *value;
+	char	*p;
+	errcode_t retval;
+	struct profile_node	*node;
+	int do_subsection = 0;
+	void *iter = 0;
+
+	state->line_num++;
+	if (state->state == STATE_GET_OBRACE) {
+		cp = skip_over_blanks(line);
+		if (*cp != '{')
+			return PROF_MISSING_OBRACE;
+		state->state = STATE_STD_LINE;
+		return 0;
+	}
+	if (state->state == STATE_INIT_COMMENT) {
+		if (line[0] != '[')
+			return 0;
+		state->state = STATE_STD_LINE;
+	}
+
+	if (*line == 0)
+		return 0;
+	strip_line(line);
+	cp = skip_over_blanks(line);
+	ch = *cp;
+	if (end_or_comment(ch))
+		return 0;
+	if (ch == '[') {
+		if (state->group_level > 0)
+			return PROF_SECTION_NOTOP;
+		cp++;
+		cp = skip_over_blanks(cp);
+		p = strchr(cp, ']');
+		if (p == NULL)
+			return PROF_SECTION_SYNTAX;
+		if (*cp == '"') {
+			cp++;
+			parse_quoted_string(cp);
+		} else {
+			*p-- = '\0';
+			while (isspace(*p) && (p > cp))
+				*p-- = '\0';
+			if (*cp == 0)
+				return PROF_SECTION_SYNTAX;
+		}
+		retval = profile_find_node(state->root_section, cp, 0, 1,
+					   &iter, &state->current_section);
+		if (retval == PROF_NO_SECTION) {
+			retval = profile_add_node(state->root_section,
+						  cp, 0,
+						  &state->current_section);
+			if (retval)
+				return retval;
+		} else if (retval)
+			return retval;
+
+		/*
+		 * Finish off the rest of the line.
+		 */
+		cp = p+1;
+		if (*cp == '*') {
+			state->current_section->final = 1;
+			cp++;
+		}
+		/*
+		 * Spaces or comments after ']' should not be fatal
+		 */
+		cp = skip_over_blanks(cp);
+		if (!end_or_comment(*cp))
+			return PROF_SECTION_SYNTAX;
+		return 0;
+	}
+	if (ch == '}') {
+		if (state->group_level == 0)
+			return PROF_EXTRA_CBRACE;
+		if (*(cp+1) == '*')
+			state->current_section->final = 1;
+		state->current_section = state->current_section->parent;
+		state->group_level--;
+		return 0;
+	}
+	/*
+	 * Parse the relations
+	 */
+	tag = cp;
+	cp = strchr(cp, '=');
+	if (!cp)
+		return PROF_RELATION_SYNTAX;
+	if (cp == tag)
+	    return PROF_RELATION_SYNTAX;
+	*cp = '\0';
+	if (*tag == '"') {
+		tag++;
+		parse_quoted_string(tag);
+	} else {
+		/* Look for whitespace on left-hand side.  */
+		p = skip_over_nonblanks(tag);
+		if (*p)
+			*p++ = 0;
+		p = skip_over_blanks(p);
+		/* If we have more non-whitespace, it's an error.  */
+		if (*p)
+			return PROF_RELATION_SYNTAX;
+	}
+
+	cp = skip_over_blanks(cp+1);
+	value = cp;
+	ch = value[0];
+	if (ch == '"') {
+		value++;
+		parse_quoted_string(value);
+	} else if (end_or_comment(ch)) {
+		do_subsection++;
+		state->state = STATE_GET_OBRACE;
+	} else if (value[0] == '{') {
+		cp = skip_over_blanks(value+1);
+		ch = *cp;
+		if (end_or_comment(ch))
+			do_subsection++;
+		else
+			return PROF_RELATION_SYNTAX;
+	} else {
+		cp = skip_over_nonblanks(value);
+		p = skip_over_blanks(cp);
+		ch = *p;
+		*cp = 0;
+		if (!end_or_comment(ch))
+			return PROF_RELATION_SYNTAX;
+	}
+	if (do_subsection) {
+		p = strchr(tag, '*');
+		if (p)
+			*p = '\0';
+		retval = profile_add_node(state->current_section,
+					  tag, 0, &state->current_section);
+		if (retval)
+			return retval;
+		if (p)
+			state->current_section->final = 1;
+		state->group_level++;
+		return 0;
+	}
+	p = strchr(tag, '*');
+	if (p)
+		*p = '\0';
+	profile_add_node(state->current_section, tag, value, &node);
+	if (p)
+		node->final = 1;
+	return 0;
+}
+
+#ifdef DEBUG_PROGRAM
+/*
+ * Return TRUE if the string begins or ends with whitespace
+ */
+static int need_double_quotes(char *str)
+{
+	if (!str || !*str)
+		return 0;
+	if (isspace((int) (*str)) ||isspace((int) (*(str + strlen(str) - 1))))
+		return 1;
+	if (strchr(str, '\n') || strchr(str, '\t') || strchr(str, '\b') ||
+	    strchr(str, ' ') || strchr(str, '#') || strchr(str, ';'))
+		return 1;
+	return 0;
+}
+
+/*
+ * Output a string with double quotes, doing appropriate backquoting
+ * of characters as necessary.
+ */
+static void output_quoted_string(char *str, void (*cb)(const char *,void *),
+				 void *data)
+{
+	char	ch;
+	char buf[2];
+
+	cb("\"", data);
+	if (!str) {
+		cb("\"", data);
+		return;
+	}
+	buf[1] = 0;
+	while ((ch = *str++)) {
+		switch (ch) {
+		case '\\':
+			cb("\\\\", data);
+			break;
+		case '\n':
+			cb("\\n", data);
+			break;
+		case '\t':
+			cb("\\t", data);
+			break;
+		case '\b':
+			cb("\\b", data);
+			break;
+		default:
+			/* This would be a lot faster if we scanned
+			   forward for the next "interesting"
+			   character.  */
+			buf[0] = ch;
+			cb(buf, data);
+			break;
+		}
+	}
+	cb("\"", data);
+}
+
+#ifndef EOL
+#define EOL "\n"
+#endif
+
+/* Errors should be returned, not ignored!  */
+static void dump_profile(struct profile_node *root, int level,
+			 void (*cb)(const char *, void *), void *data)
+{
+	int i;
+	struct profile_node *p;
+	void *iter;
+	long retval;
+
+	iter = 0;
+	do {
+		retval = profile_find_node(root, 0, 0, 0, &iter, &p);
+		if (retval)
+			break;
+		for (i=0; i < level; i++)
+			cb("\t", data);
+		if (need_double_quotes(p->name))
+			output_quoted_string(p->name, cb, data);
+		else
+			cb(p->name, data);
+		cb(" = ", data);
+		if (need_double_quotes(p->value))
+			output_quoted_string(p->value, cb, data);
+		else
+			cb(p->value, data);
+		cb(EOL, data);
+	} while (iter != 0);
+
+	iter = 0;
+	do {
+		retval = profile_find_node(root, 0, 0, 1, &iter, &p);
+		if (retval)
+			break;
+		if (level == 0)	{ /* [xxx] */
+			cb("[", data);
+			if (need_double_quotes(p->name))
+				output_quoted_string(p->name, cb, data);
+			else
+				cb(p->name, data);
+			cb("]", data);
+			cb(p->final ? "*" : "", data);
+			cb(EOL, data);
+			dump_profile(p, level+1, cb, data);
+			cb(EOL, data);
+		} else { 	/* xxx = { ... } */
+			for (i=0; i < level; i++)
+				cb("\t", data);
+			if (need_double_quotes(p->name))
+				output_quoted_string(p->name, cb, data);
+			else
+				cb(p->name, data);
+			cb(" = {", data);
+			cb(EOL, data);
+			dump_profile(p, level+1, cb, data);
+			for (i=0; i < level; i++)
+				cb("\t", data);
+			cb("}", data);
+			cb(p->final ? "*" : "", data);
+			cb(EOL, data);
+		}
+	} while (iter != 0);
+}
+
+static void dump_profile_to_file_cb(const char *str, void *data)
+{
+	fputs(str, data);
+}
+
+errcode_t profile_write_tree_file(struct profile_node *root, FILE *dstfile)
+{
+	dump_profile(root, 0, dump_profile_to_file_cb, dstfile);
+	return 0;
+}
+
+struct prof_buf {
+	char *base;
+	size_t cur, max;
+	int err;
+};
+
+static void add_data_to_buffer(struct prof_buf *b, const void *d, size_t len)
+{
+	if (b->err)
+		return;
+	if (b->max - b->cur < len) {
+		size_t newsize;
+		char *newptr;
+
+		newsize = b->max + (b->max >> 1) + len + 1024;
+		newptr = realloc(b->base, newsize);
+		if (newptr == NULL) {
+			b->err = 1;
+			return;
+		}
+		b->base = newptr;
+		b->max = newsize;
+	}
+	memcpy(b->base + b->cur, d, len);
+	b->cur += len; 		/* ignore overflow */
+}
+
+static void dump_profile_to_buffer_cb(const char *str, void *data)
+{
+	add_data_to_buffer((struct prof_buf *)data, str, strlen(str));
+}
+
+errcode_t profile_write_tree_to_buffer(struct profile_node *root,
+				       char **buf)
+{
+	struct prof_buf prof_buf = { 0, 0, 0, 0 };
+
+	dump_profile(root, 0, dump_profile_to_buffer_cb, &prof_buf);
+	if (prof_buf.err) {
+		*buf = NULL;
+		return ENOMEM;
+	}
+	add_data_to_buffer(&prof_buf, "", 1); /* append nul */
+	if (prof_buf.max - prof_buf.cur > (prof_buf.max >> 3)) {
+		char *newptr = realloc(prof_buf.base, prof_buf.cur);
+		if (newptr)
+			prof_buf.base = newptr;
+	}
+	*buf = prof_buf.base;
+	return 0;
+}
+#endif
+
+/*
+ * prof_tree.c --- these routines maintain the parse tree of the
+ * 	config file.
+ *
+ * All of the details of how the tree is stored is abstracted away in
+ * this file; all of the other profile routines build, access, and
+ * modify the tree via the accessor functions found in this file.
+ *
+ * Each node may represent either a relation or a section header.
+ *
+ * A section header must have its value field set to 0, and may a one
+ * or more child nodes, pointed to by first_child.
+ *
+ * A relation has as its value a pointer to allocated memory
+ * containing a string.  Its first_child pointer must be null.
+ *
+ */
+
+/*
+ * Free a node, and any children
+ */
+void profile_free_node(struct profile_node *node)
+{
+	struct profile_node *child, *next;
+
+	if (node->magic != PROF_MAGIC_NODE)
+		return;
+
+	free(node->name);
+	free(node->value);
+
+	for (child=node->first_child; child; child = next) {
+		next = child->next;
+		profile_free_node(child);
+	}
+	node->magic = 0;
+
+	free(node);
+}
+
+#ifndef HAVE_STRDUP
+#undef strdup
+#define strdup MYstrdup
+static char *MYstrdup (const char *s)
+{
+    size_t sz = strlen(s) + 1;
+    char *p = malloc(sz);
+    if (p != 0)
+	memcpy(p, s, sz);
+    return p;
+}
+#endif
+
+/*
+ * Create a node
+ */
+errcode_t profile_create_node(const char *name, const char *value,
+			      struct profile_node **ret_node)
+{
+	struct profile_node *new;
+
+	new = malloc(sizeof(struct profile_node));
+	if (!new)
+		return ENOMEM;
+	memset(new, 0, sizeof(struct profile_node));
+	new->name = strdup(name);
+	if (new->name == 0) {
+	    profile_free_node(new);
+	    return ENOMEM;
+	}
+	if (value) {
+		new->value = strdup(value);
+		if (new->value == 0) {
+		    profile_free_node(new);
+		    return ENOMEM;
+		}
+	}
+	new->magic = PROF_MAGIC_NODE;
+
+	*ret_node = new;
+	return 0;
+}
+
+/*
+ * This function verifies that all of the representation invarients of
+ * the profile are true.  If not, we have a programming bug somewhere,
+ * probably in this file.
+ */
+#ifdef DEBUG_PROGRAM
+errcode_t profile_verify_node(struct profile_node *node)
+{
+	struct profile_node *p, *last;
+	errcode_t	retval;
+
+	CHECK_MAGIC(node);
+
+	if (node->value && node->first_child)
+		return PROF_SECTION_WITH_VALUE;
+
+	last = 0;
+	for (p = node->first_child; p; last = p, p = p->next) {
+		if (p->prev != last)
+			return PROF_BAD_LINK_LIST;
+		if (last && (last->next != p))
+			return PROF_BAD_LINK_LIST;
+		if (node->group_level+1 != p->group_level)
+			return PROF_BAD_GROUP_LVL;
+		if (p->parent != node)
+			return PROF_BAD_PARENT_PTR;
+		retval = profile_verify_node(p);
+		if (retval)
+			return retval;
+	}
+	return 0;
+}
+#endif
+
+/*
+ * Add a node to a particular section
+ */
+errcode_t profile_add_node(struct profile_node *section, const char *name,
+			   const char *value, struct profile_node **ret_node)
+{
+	errcode_t retval;
+	struct profile_node *p, *last, *new;
+
+	CHECK_MAGIC(section);
+
+	if (section->value)
+		return PROF_ADD_NOT_SECTION;
+
+	/*
+	 * Find the place to insert the new node.  We look for the
+	 * place *after* the last match of the node name, since
+	 * order matters.
+	 */
+	for (p=section->first_child, last = 0; p; last = p, p = p->next) {
+		int cmp;
+		cmp = strcmp(p->name, name);
+		if (cmp > 0)
+			break;
+	}
+	retval = profile_create_node(name, value, &new);
+	if (retval)
+		return retval;
+	new->group_level = section->group_level+1;
+	new->deleted = 0;
+	new->parent = section;
+	new->prev = last;
+	new->next = p;
+	if (p)
+		p->prev = new;
+	if (last)
+		last->next = new;
+	else
+		section->first_child = new;
+	if (ret_node)
+		*ret_node = new;
+	return 0;
+}
+
+/*
+ * Iterate through the section, returning the nodes which match
+ * the given name.  If name is NULL, then interate through all the
+ * nodes in the section.  If section_flag is non-zero, only return the
+ * section which matches the name; don't return relations.  If value
+ * is non-NULL, then only return relations which match the requested
+ * value.  (The value argument is ignored if section_flag is non-zero.)
+ *
+ * The first time this routine is called, the state pointer must be
+ * null.  When this profile_find_node_relation() returns, if the state
+ * pointer is non-NULL, then this routine should be called again.
+ * (This won't happen if section_flag is non-zero, obviously.)
+ *
+ */
+errcode_t profile_find_node(struct profile_node *section, const char *name,
+			    const char *value, int section_flag, void **state,
+			    struct profile_node **node)
+{
+	struct profile_node *p;
+
+	CHECK_MAGIC(section);
+	p = *state;
+	if (p) {
+		CHECK_MAGIC(p);
+	} else
+		p = section->first_child;
+
+	for (; p; p = p->next) {
+		if (name && (strcmp(p->name, name)))
+			continue;
+		if (section_flag) {
+			if (p->value)
+				continue;
+		} else {
+			if (!p->value)
+				continue;
+			if (value && (strcmp(p->value, value)))
+				continue;
+		}
+		if (p->deleted)
+		    continue;
+		/* A match! */
+		if (node)
+			*node = p;
+		break;
+	}
+	if (p == 0) {
+		*state = 0;
+		return section_flag ? PROF_NO_SECTION : PROF_NO_RELATION;
+	}
+	/*
+	 * OK, we've found one match; now let's try to find another
+	 * one.  This way, if we return a non-zero state pointer,
+	 * there's guaranteed to be another match that's returned.
+	 */
+	for (p = p->next; p; p = p->next) {
+		if (name && (strcmp(p->name, name)))
+			continue;
+		if (section_flag) {
+			if (p->value)
+				continue;
+		} else {
+			if (!p->value)
+				continue;
+			if (value && (strcmp(p->value, value)))
+				continue;
+		}
+		/* A match! */
+		break;
+	}
+	*state = p;
+	return 0;
+}
+
+/*
+ * This is a general-purpose iterator for returning all nodes that
+ * match the specified name array.
+ */
+struct profile_iterator {
+	prf_magic_t		magic;
+	profile_t		profile;
+	int			flags;
+	const char 		*const *names;
+	const char		*name;
+	prf_file_t		file;
+	int			file_serial;
+	int			done_idx;
+	struct profile_node 	*node;
+	int			num;
+};
+
+errcode_t
+profile_iterator_create(profile_t profile, const char *const *names, int flags,
+			void **ret_iter)
+{
+	struct profile_iterator *iter;
+	int	done_idx = 0;
+
+	if (profile == 0)
+		return PROF_NO_PROFILE;
+	if (profile->magic != PROF_MAGIC_PROFILE)
+		return PROF_MAGIC_PROFILE;
+	if (!names)
+		return PROF_BAD_NAMESET;
+	if (!(flags & PROFILE_ITER_LIST_SECTION)) {
+		if (!names[0])
+			return PROF_BAD_NAMESET;
+		done_idx = 1;
+	}
+
+	if ((iter = malloc(sizeof(struct profile_iterator))) == NULL)
+		return ENOMEM;
+
+	iter->magic = PROF_MAGIC_ITERATOR;
+	iter->profile = profile;
+	iter->names = names;
+	iter->flags = flags;
+	iter->file = profile->first_file;
+	iter->done_idx = done_idx;
+	iter->node = 0;
+	iter->num = 0;
+	*ret_iter = iter;
+	return 0;
+}
+
+void profile_iterator_free(void **iter_p)
+{
+	struct profile_iterator *iter;
+
+	if (!iter_p)
+		return;
+	iter = *iter_p;
+	if (!iter || iter->magic != PROF_MAGIC_ITERATOR)
+		return;
+	free(iter);
+	*iter_p = 0;
+}
+
+/*
+ * Note: the returned character strings in ret_name and ret_value
+ * points to the stored character string in the parse string.  Before
+ * this string value is returned to a calling application
+ * (profile_node_iterator is not an exported interface), it should be
+ * strdup()'ed.
+ */
+errcode_t profile_node_iterator(void **iter_p, struct profile_node **ret_node,
+				char **ret_name, char **ret_value)
+{
+	struct profile_iterator 	*iter = *iter_p;
+	struct profile_node 		*section, *p;
+	const char			*const *cpp;
+	errcode_t			retval;
+	int				skip_num = 0;
+
+	if (!iter || iter->magic != PROF_MAGIC_ITERATOR)
+		return PROF_MAGIC_ITERATOR;
+	if (iter->file && iter->file->magic != PROF_MAGIC_FILE)
+	    return PROF_MAGIC_FILE;
+	/*
+	 * If the file has changed, then the node pointer is invalid,
+	 * so we'll have search the file again looking for it.
+	 */
+	if (iter->node && (iter->file &&
+			   iter->file->upd_serial != iter->file_serial)) {
+		iter->flags &= ~PROFILE_ITER_FINAL_SEEN;
+		skip_num = iter->num;
+		iter->node = 0;
+	}
+	if (iter->node && iter->node->magic != PROF_MAGIC_NODE) {
+	    return PROF_MAGIC_NODE;
+	}
+get_new_file:
+	if (iter->node == 0) {
+		if (iter->file == 0 ||
+		    (iter->flags & PROFILE_ITER_FINAL_SEEN)) {
+			profile_iterator_free(iter_p);
+			if (ret_node)
+				*ret_node = 0;
+			if (ret_name)
+				*ret_name = 0;
+			if (ret_value)
+				*ret_value =0;
+			return 0;
+		}
+		if ((retval = profile_update_file(iter->file))) {
+		    if (retval == ENOENT || retval == EACCES) {
+			/* XXX memory leak? */
+			iter->file = iter->file->next;
+			skip_num = 0;
+			retval = 0;
+			goto get_new_file;
+		    } else {
+			profile_iterator_free(iter_p);
+			return retval;
+		    }
+		}
+		iter->file_serial = iter->file->upd_serial;
+		/*
+		 * Find the section to list if we are a LIST_SECTION,
+		 * or find the containing section if not.
+		 */
+		section = iter->file->root;
+		for (cpp = iter->names; cpp[iter->done_idx]; cpp++) {
+			for (p=section->first_child; p; p = p->next) {
+				if (!strcmp(p->name, *cpp) && !p->value)
+					break;
+			}
+			if (!p) {
+				section = 0;
+				break;
+			}
+			section = p;
+			if (p->final)
+				iter->flags |= PROFILE_ITER_FINAL_SEEN;
+		}
+		if (!section) {
+			iter->file = iter->file->next;
+			skip_num = 0;
+			goto get_new_file;
+		}
+		iter->name = *cpp;
+		iter->node = section->first_child;
+	}
+	/*
+	 * OK, now we know iter->node is set up correctly.  Let's do
+	 * the search.
+	 */
+	for (p = iter->node; p; p = p->next) {
+		if (iter->name && strcmp(p->name, iter->name))
+			continue;
+		if ((iter->flags & PROFILE_ITER_SECTIONS_ONLY) &&
+		    p->value)
+			continue;
+		if ((iter->flags & PROFILE_ITER_RELATIONS_ONLY) &&
+		    !p->value)
+			continue;
+		if (skip_num > 0) {
+			skip_num--;
+			continue;
+		}
+		if (p->deleted)
+			continue;
+		break;
+	}
+	iter->num++;
+	if (!p) {
+		iter->file = iter->file->next;
+		iter->node = 0;
+		skip_num = 0;
+		goto get_new_file;
+	}
+	if ((iter->node = p->next) == NULL)
+		iter->file = iter->file->next;
+	if (ret_node)
+		*ret_node = p;
+	if (ret_name)
+		*ret_name = p->name;
+	if (ret_value)
+		*ret_value = p->value;
+	return 0;
+}
+
+
+/*
+ * prof_get.c --- routines that expose the public interfaces for
+ * 	querying items from the profile.
+ *
+ */
+
+/*
+ * This function only gets the first value from the file; it is a
+ * helper function for profile_get_string, profile_get_integer, etc.
+ */
+errcode_t profile_get_value(profile_t profile, const char *name,
+			    const char *subname, const char *subsubname,
+			    const char **ret_value)
+{
+	errcode_t		retval;
+	void			*state;
+	char			*value;
+	const char		*names[4];
+
+	names[0] = name;
+	names[1] = subname;
+	names[2] = subsubname;
+	names[3] = 0;
+
+	if ((retval = profile_iterator_create(profile, names,
+					      PROFILE_ITER_RELATIONS_ONLY,
+					      &state)))
+		return retval;
+
+	if ((retval = profile_node_iterator(&state, 0, 0, &value)))
+		goto cleanup;
+
+	if (value)
+		*ret_value = value;
+	else
+		retval = PROF_NO_RELATION;
+
+cleanup:
+	profile_iterator_free(&state);
+	return retval;
+}
+
+errcode_t
+profile_get_string(profile_t profile, const char *name, const char *subname,
+		   const char *subsubname, const char *def_val,
+		   char **ret_string)
+{
+	const char	*value;
+	errcode_t	retval;
+
+	if (profile) {
+		retval = profile_get_value(profile, name, subname,
+					   subsubname, &value);
+		if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION)
+			value = def_val;
+		else if (retval)
+			return retval;
+	} else
+		value = def_val;
+
+	if (value) {
+		*ret_string = malloc(strlen(value)+1);
+		if (*ret_string == 0)
+			return ENOMEM;
+		strcpy(*ret_string, value);
+	} else
+		*ret_string = 0;
+	return 0;
+}
+
+errcode_t
+profile_get_integer(profile_t profile, const char *name, const char *subname,
+		    const char *subsubname, int def_val, int *ret_int)
+{
+	const char	*value;
+	errcode_t	retval;
+	char            *end_value;
+	long		ret_long;
+
+	*ret_int = def_val;
+	if (profile == 0)
+		return 0;
+
+	retval = profile_get_value(profile, name, subname, subsubname, &value);
+	if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) {
+		*ret_int = def_val;
+		return 0;
+	} else if (retval)
+		return retval;
+
+	if (value[0] == 0)
+	    /* Empty string is no good.  */
+	    return PROF_BAD_INTEGER;
+	errno = 0;
+	ret_long = strtol(value, &end_value, 0);
+
+	/* Overflow or underflow.  */
+	if ((ret_long == LONG_MIN || ret_long == LONG_MAX) && errno != 0)
+	    return PROF_BAD_INTEGER;
+	/* Value outside "int" range.  */
+	if ((long) (int) ret_long != ret_long)
+	    return PROF_BAD_INTEGER;
+	/* Garbage in string.  */
+	if (end_value != value + strlen (value))
+	    return PROF_BAD_INTEGER;
+
+
+	*ret_int = ret_long;
+	return 0;
+}
+
+errcode_t
+profile_get_uint(profile_t profile, const char *name, const char *subname,
+		 const char *subsubname, unsigned int def_val,
+		 unsigned int *ret_int)
+{
+	const char	*value;
+	errcode_t	retval;
+	char            *end_value;
+	unsigned long	ret_long;
+
+	*ret_int = def_val;
+	if (profile == 0)
+		return 0;
+
+	retval = profile_get_value(profile, name, subname, subsubname, &value);
+	if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) {
+		*ret_int = def_val;
+		return 0;
+	} else if (retval)
+		return retval;
+
+	if (value[0] == 0)
+	    /* Empty string is no good.  */
+	    return PROF_BAD_INTEGER;
+	errno = 0;
+	ret_long = strtoul(value, &end_value, 0);
+
+	/* Overflow or underflow.  */
+	if ((ret_long == ULONG_MAX) && errno != 0)
+	    return PROF_BAD_INTEGER;
+	/* Value outside "int" range.  */
+	if ((unsigned long) (unsigned int) ret_long != ret_long)
+	    return PROF_BAD_INTEGER;
+	/* Garbage in string.  */
+	if (end_value != value + strlen (value))
+	    return PROF_BAD_INTEGER;
+
+	*ret_int = ret_long;
+	return 0;
+}
+
+errcode_t
+profile_get_double(profile_t profile, const char *name, const char *subname,
+		   const char *subsubname, double def_val, double *ret_double)
+{
+	const char	*value;
+	errcode_t	  retval;
+	char        *end_value;
+	double      double_val;
+
+	*ret_double = def_val;
+	if (profile == 0)
+		return 0;
+
+	retval = profile_get_value(profile, name, subname, subsubname, &value);
+	if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) {
+		*ret_double = def_val;
+		return 0;
+	} else if (retval)
+		return retval;
+
+	if (value[0] == 0)
+		/* Empty string is no good.  */
+		return PROF_BAD_INTEGER;
+	errno = 0;
+	double_val = strtod(value, &end_value);
+
+	/* Overflow or underflow.  */
+	if (errno != 0)
+		return PROF_BAD_INTEGER;
+	/* Garbage in string.  */
+	if (end_value != value + strlen(value))
+		return PROF_BAD_INTEGER;
+
+	*ret_double = double_val;
+	return 0;
+}
+
+static const char *const conf_yes[] = {
+    "y", "yes", "true", "t", "1", "on",
+    0,
+};
+
+static const char *const conf_no[] = {
+    "n", "no", "false", "nil", "0", "off",
+    0,
+};
+
+static errcode_t
+profile_parse_boolean(const char *s, int *ret_boolean)
+{
+    const char *const *p;
+
+    if (ret_boolean == NULL)
+	return PROF_EINVAL;
+
+    for(p=conf_yes; *p; p++) {
+		if (!strcasecmp(*p,s)) {
+			*ret_boolean = 1;
+			return 0;
+		}
+    }
+
+    for(p=conf_no; *p; p++) {
+		if (!strcasecmp(*p,s)) {
+			*ret_boolean = 0;
+			return 0;
+		}
+    }
+
+	return PROF_BAD_BOOLEAN;
+}
+
+errcode_t
+profile_get_boolean(profile_t profile, const char *name, const char *subname,
+		    const char *subsubname, int def_val, int *ret_boolean)
+{
+	const char	*value;
+	errcode_t	retval;
+
+	if (profile == 0) {
+		*ret_boolean = def_val;
+		return 0;
+	}
+
+	retval = profile_get_value(profile, name, subname, subsubname, &value);
+	if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) {
+		*ret_boolean = def_val;
+		return 0;
+	} else if (retval)
+		return retval;
+
+	return profile_parse_boolean (value, ret_boolean);
+}
+
+errcode_t
+profile_iterator(void **iter_p, char **ret_name, char **ret_value)
+{
+	char *name, *value;
+	errcode_t	retval;
+
+	retval = profile_node_iterator(iter_p, 0, &name, &value);
+	if (retval)
+		return retval;
+
+	if (ret_name) {
+		if (name) {
+			*ret_name = malloc(strlen(name)+1);
+			if (!*ret_name)
+				return ENOMEM;
+			strcpy(*ret_name, name);
+		} else
+			*ret_name = 0;
+	}
+	if (ret_value) {
+		if (value) {
+			*ret_value = malloc(strlen(value)+1);
+			if (!*ret_value) {
+				if (ret_name) {
+					free(*ret_name);
+					*ret_name = 0;
+				}
+				return ENOMEM;
+			}
+			strcpy(*ret_value, value);
+		} else
+			*ret_value = 0;
+	}
+	return 0;
+}
+
+#ifdef DEBUG_PROGRAM
+
+/*
+ * test_profile.c --- testing program for the profile routine
+ */
+
+#include "argv_parse.h"
+#include "profile_helpers.h"
+
+const char *program_name = "test_profile";
+
+#define PRINT_VALUE	1
+#define PRINT_VALUES	2
+
+static void do_cmd(profile_t profile, char **argv)
+{
+	errcode_t	retval;
+	const char	**names, *value;
+	char		**values, **cpp;
+	char	*cmd;
+	int		print_status;
+
+	cmd = *(argv);
+	names = (const char **) argv + 1;
+	print_status = 0;
+	retval = 0;
+	if (cmd == 0)
+		return;
+	if (!strcmp(cmd, "query")) {
+		retval = profile_get_values(profile, names, &values);
+		print_status = PRINT_VALUES;
+	} else if (!strcmp(cmd, "query1")) {
+		const char *name = 0;
+		const char *subname = 0;
+		const char *subsubname = 0;
+
+		name = names[0];
+		if (name)
+			subname = names[1];
+		if (subname)
+			subsubname = names[2];
+		if (subsubname && names[3]) {
+			fprintf(stderr,
+				"Only 3 levels are allowed with query1\n");
+			retval = EINVAL;
+		} else
+			retval = profile_get_value(profile, name, subname,
+						   subsubname, &value);
+		print_status = PRINT_VALUE;
+	} else if (!strcmp(cmd, "list_sections")) {
+		retval = profile_get_subsection_names(profile, names,
+						      &values);
+		print_status = PRINT_VALUES;
+	} else if (!strcmp(cmd, "list_relations")) {
+		retval = profile_get_relation_names(profile, names,
+						    &values);
+		print_status = PRINT_VALUES;
+	} else if (!strcmp(cmd, "dump")) {
+		retval = profile_write_tree_file
+			(profile->first_file->root, stdout);
+#if 0
+	} else if (!strcmp(cmd, "clear")) {
+		retval = profile_clear_relation(profile, names);
+	} else if (!strcmp(cmd, "update")) {
+		retval = profile_update_relation(profile, names+2,
+						 *names, *(names+1));
+#endif
+	} else if (!strcmp(cmd, "verify")) {
+		retval = profile_verify_node
+			(profile->first_file->root);
+#if 0
+	} else if (!strcmp(cmd, "rename_section")) {
+		retval = profile_rename_section(profile, names+1, *names);
+	} else if (!strcmp(cmd, "add")) {
+		value = *names;
+		if (strcmp(value, "NULL") == 0)
+			value = NULL;
+		retval = profile_add_relation(profile, names+1, value);
+	} else if (!strcmp(cmd, "flush")) {
+		retval = profile_flush(profile);
+#endif
+	} else {
+		printf("Invalid command.\n");
+	}
+	if (retval) {
+		com_err(cmd, retval, "");
+		print_status = 0;
+	}
+	switch (print_status) {
+	case PRINT_VALUE:
+		printf("%s\n", value);
+		break;
+	case PRINT_VALUES:
+		for (cpp = values; *cpp; cpp++)
+			printf("%s\n", *cpp);
+		profile_free_list(values);
+		break;
+	}
+}
+
+static void do_batchmode(profile_t profile)
+{
+	int		argc, ret;
+	char		**argv;
+	char		buf[256];
+
+	while (!feof(stdin)) {
+		if (fgets(buf, sizeof(buf), stdin) == NULL)
+			break;
+		printf(">%s", buf);
+		ret = argv_parse(buf, &argc, &argv);
+		if (ret != 0) {
+			printf("Argv_parse returned %d!\n", ret);
+			continue;
+		}
+		do_cmd(profile, argv);
+		printf("\n");
+		argv_free(argv);
+	}
+	profile_release(profile);
+	exit(0);
+
+}
+
+void syntax_err_report(const char *filename, long err, int line_num)
+{
+	fprintf(stderr, "Syntax error in %s, line number %d: %s\n",
+		filename, line_num, error_message(err));
+	exit(1);
+}
+
+const char *default_str = "[foo]\n\tbar=quux\n\tsub = {\n\t\twin = true\n}\n";
+
+int main(int argc, char **argv)
+{
+    profile_t	profile;
+    long	retval;
+    char	*cmd;
+
+    if (argc < 2) {
+	    fprintf(stderr, "Usage: %s filename [cmd argset]\n", program_name);
+	    exit(1);
+    }
+
+    initialize_prof_error_table();
+
+    profile_set_syntax_err_cb(syntax_err_report);
+
+    retval = profile_init_path(argv[1], &profile);
+    if (retval) {
+	com_err(program_name, retval, "while initializing profile");
+	exit(1);
+    }
+    retval = profile_set_default(profile, default_str);
+    if (retval) {
+	com_err(program_name, retval, "while setting default");
+	exit(1);
+    }
+
+    cmd = *(argv+2);
+    if (!cmd || !strcmp(cmd, "batch"))
+	    do_batchmode(profile);
+    else
+	    do_cmd(profile, argv+2);
+    profile_release(profile);
+
+    return 0;
+}
+
+#endif
diff --git a/mkfs/profile.h b/mkfs/profile.h
new file mode 100644
index 0000000..90a81fc
--- /dev/null
+++ b/mkfs/profile.h
@@ -0,0 +1,120 @@
+/*
+ * profile.h
+ *
+ * Copyright (C) 2005 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ * Copyright (C) 1985-2005 by the Massachusetts Institute of Technology.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government.  It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original MIT software.
+ * M.I.T. makes no representations about the suitability of this software
+ * for any purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef _PROFILE_H
+#define _PROFILE_H
+
+#include <sys/stat.h>
+
+typedef struct _profile_t *profile_t;
+
+typedef void (*profile_syntax_err_cb_t)(const char *file, long err,
+					int line_num);
+
+/*
+ * Used by the profile iterator in prof_get.c
+ */
+#define PROFILE_ITER_LIST_SECTION	0x0001
+#define PROFILE_ITER_SECTIONS_ONLY	0x0002
+#define PROFILE_ITER_RELATIONS_ONLY	0x0004
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+long profile_init
+	(const char * const *files, profile_t *ret_profile);
+
+long profile_get_stat
+	(profile_t profile, struct stat *st);
+
+void profile_release
+	(profile_t profile);
+
+long profile_set_default
+	(profile_t profile, const char *def_string);
+
+long profile_get_string
+	(profile_t profile, const char *name, const char *subname,
+			const char *subsubname, const char *def_val,
+			char **ret_string);
+long profile_get_integer
+	(profile_t profile, const char *name, const char *subname,
+			const char *subsubname, int def_val,
+			int *ret_default);
+
+long profile_get_uint
+	(profile_t profile, const char *name, const char *subname,
+		const char *subsubname, unsigned int def_val,
+		unsigned int *ret_int);
+
+long profile_get_double
+	(profile_t profile, const char *name, const char *subname,
+		const char *subsubname, double def_val,
+		double *ret_float);
+
+long profile_get_boolean
+	(profile_t profile, const char *name, const char *subname,
+			const char *subsubname, int def_val,
+			int *ret_default);
+
+long profile_iterator_create
+	(profile_t profile, const char *const *names,
+		   int flags, void **ret_iter);
+
+void profile_iterator_free
+	(void **iter_p);
+
+long profile_iterator
+	(void	**iter_p, char **ret_name, char **ret_value);
+
+/* profile_helpers.c helpers */
+long profile_get_values
+	(profile_t profile, const char *const *names, char ***ret_values);
+long profile_get_subsection_names
+	(profile_t profile, const char **names, char ***ret_names);
+long profile_get_relation_names
+	(profile_t profile, const char **names, char ***ret_names);
+
+profile_syntax_err_cb_t profile_set_syntax_err_cb(profile_syntax_err_cb_t hook);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _KRB5_H */
diff --git a/mkfs/profile_helpers.c b/mkfs/profile_helpers.c
new file mode 100644
index 0000000..df7bc39
--- /dev/null
+++ b/mkfs/profile_helpers.c
@@ -0,0 +1,319 @@
+/*
+ * profile_helpers.c -- Helper functions for the profile library
+ *
+ * These functions are not part of the "core" profile library, and do
+ * not require access to the internal functions and data structures of
+ * the profile library.  They are mainly convenience functions for
+ * programs that want to do something unusual such as obtaining the
+ * list of sections or relations, or accessing multiple values from a
+ * relation that is listed more than once.  This functionality can all
+ * be done using the profile_iterator abstraction, but it is less
+ * convenient.
+ *
+ * Copyright (C) 2006 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#ifdef PROFILE_USE_CONFIG
+#include "config.h"
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <et/com_err.h>
+#include "profile.h"
+#include "profile_helpers.h"
+#include "prof_err.h"
+
+/*
+ * These functions --- init_list(), end_list(), and add_to_list() are
+ * internal functions used to build up a null-terminated char ** list
+ * of strings to be returned by functions like profile_get_values.
+ *
+ * The profile_string_list structure is used for internal booking
+ * purposes to build up the list, which is returned in *ret_list by
+ * the end_list() function.
+ *
+ * The publicly exported interface for freeing char** list is
+ * profile_free_list().
+ */
+
+struct profile_string_list {
+	char	**list;
+	int	num;
+	int	max;
+};
+
+/*
+ * Initialize the string list abstraction.
+ */
+static errcode_t init_list(struct profile_string_list *list)
+{
+	list->num = 0;
+	list->max = 10;
+	list->list = malloc(list->max * sizeof(char *));
+	if (list->list == 0)
+		return ENOMEM;
+	list->list[0] = 0;
+	return 0;
+}
+
+/*
+ * Free any memory left over in the string abstraction, returning the
+ * built up list in *ret_list if it is non-null.
+ */
+static void end_list(struct profile_string_list *list, char ***ret_list)
+{
+	char	**cp;
+
+	if (list == 0)
+		return;
+
+	if (ret_list) {
+		*ret_list = list->list;
+		return;
+	} else {
+		for (cp = list->list; *cp; cp++)
+			free(*cp);
+		free(list->list);
+	}
+	list->num = list->max = 0;
+	list->list = 0;
+}
+
+/*
+ * Add a string to the list.
+ */
+static errcode_t add_to_list(struct profile_string_list *list, char *str)
+{
+	char 	**newlist;
+	int	newmax;
+
+	if (list->num+1 >= list->max) {
+		newmax = list->max + 10;
+		newlist = realloc(list->list, newmax * sizeof(char *));
+		if (newlist == 0)
+			return ENOMEM;
+		list->max = newmax;
+		list->list = newlist;
+	}
+
+	list->list[list->num++] = str;
+	list->list[list->num] = 0;
+	return 0;
+}
+
+/*
+ * Return TRUE if the string is already a member of the list.
+ */
+static int is_list_member(struct profile_string_list *list, const char *str)
+{
+	char **cpp;
+
+	if (!list->list)
+		return 0;
+
+	for (cpp = list->list; *cpp; cpp++) {
+		if (!strcmp(*cpp, str))
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * This function frees a null-terminated list as returned by
+ * profile_get_values.
+ */
+void profile_free_list(char **list)
+{
+    char	**cp;
+
+    if (list == 0)
+	    return;
+
+    for (cp = list; *cp; cp++)
+	free(*cp);
+    free(list);
+}
+
+errcode_t
+profile_get_values(profile_t profile, const char *const *names,
+		   char ***ret_values)
+{
+	errcode_t		retval;
+	void			*state;
+	char			*value;
+	struct profile_string_list values;
+
+	if ((retval = profile_iterator_create(profile, names,
+					      PROFILE_ITER_RELATIONS_ONLY,
+					      &state)))
+		return retval;
+
+	if ((retval = init_list(&values)))
+		goto cleanup_iterator;
+
+	do {
+		if ((retval = profile_iterator(&state, 0, &value)))
+			goto cleanup;
+		if (value)
+			add_to_list(&values, value);
+	} while (state);
+
+	if (values.num == 0) {
+		retval = PROF_NO_RELATION;
+		goto cleanup;
+	}
+
+	end_list(&values, ret_values);
+	return 0;
+
+cleanup:
+	end_list(&values, 0);
+cleanup_iterator:
+	profile_iterator_free(&state);
+	return retval;
+}
+
+/*
+ * This function will return the list of the names of subections in the
+ * under the specified section name.
+ */
+errcode_t
+profile_get_subsection_names(profile_t profile, const char **names,
+			     char ***ret_names)
+{
+	errcode_t		retval;
+	void			*state;
+	char			*name;
+	struct profile_string_list values;
+
+	if ((retval = profile_iterator_create(profile, names,
+		   PROFILE_ITER_LIST_SECTION | PROFILE_ITER_SECTIONS_ONLY,
+		   &state)))
+		return retval;
+
+	if ((retval = init_list(&values)))
+		goto cleanup_iterator;
+
+	do {
+		if ((retval = profile_iterator(&state, &name, 0)))
+			goto cleanup;
+		if (name)
+			add_to_list(&values, name);
+	} while (state);
+
+	end_list(&values, ret_names);
+	return 0;
+
+cleanup:
+	end_list(&values, 0);
+cleanup_iterator:
+	profile_iterator_free(&state);
+	return retval;
+}
+
+/*
+ * This function will return the list of the names of relations in the
+ * under the specified section name.
+ */
+errcode_t
+profile_get_relation_names(profile_t profile, const char **names,
+			   char ***ret_names)
+{
+	errcode_t		retval;
+	void			*state;
+	char			*name;
+	struct profile_string_list values;
+
+	if ((retval = profile_iterator_create(profile, names,
+		   PROFILE_ITER_LIST_SECTION | PROFILE_ITER_RELATIONS_ONLY,
+		   &state)))
+		return retval;
+
+	if ((retval = init_list(&values)))
+		goto cleanup_iterator;
+
+	do {
+		if ((retval = profile_iterator(&state, &name, 0)))
+			goto cleanup;
+		if (name) {
+			if (is_list_member(&values, name))
+				free(name);
+			else
+				add_to_list(&values, name);
+		}
+	} while (state);
+
+	end_list(&values, ret_names);
+	return 0;
+
+cleanup:
+	end_list(&values, 0);
+cleanup_iterator:
+	profile_iterator_free(&state);
+	return retval;
+}
+
+
+void
+profile_release_string(char *str)
+{
+	free(str);
+}
+
+errcode_t
+profile_init_path(const char * filepath,
+		  profile_t *ret_profile)
+{
+	int n_entries, i;
+	unsigned int ent_len;
+	const char *s, *t;
+	char **filenames;
+	errcode_t retval;
+
+	/* count the distinct filename components */
+	for(s = filepath, n_entries = 1; *s; s++) {
+		if (*s == ':')
+			n_entries++;
+	}
+
+	/* the array is NULL terminated */
+	filenames = (char **) malloc((n_entries+1) * sizeof(char*));
+	if (filenames == 0)
+		return ENOMEM;
+
+	/* measure, copy, and skip each one */
+	for(s = filepath, i=0; (t = strchr(s, ':')) || (t=s+strlen(s)); s=t+1, i++) {
+		ent_len = t-s;
+		filenames[i] = (char*) malloc(ent_len + 1);
+		if (filenames[i] == 0) {
+			/* if malloc fails, free the ones that worked */
+			while(--i >= 0) free(filenames[i]);
+                        free(filenames);
+			return ENOMEM;
+		}
+		strncpy(filenames[i], s, ent_len);
+		filenames[i][ent_len] = 0;
+		if (*t == 0) {
+			i++;
+			break;
+		}
+	}
+	/* cap the array */
+	filenames[i] = 0;
+
+	retval = profile_init((const char * const *) filenames,
+			      ret_profile);
+
+	/* count back down and free the entries */
+	while(--i >= 0) free(filenames[i]);
+	free(filenames);
+
+	return retval;
+}
diff --git a/mkfs/profile_helpers.h b/mkfs/profile_helpers.h
new file mode 100644
index 0000000..334a71d
--- /dev/null
+++ b/mkfs/profile_helpers.h
@@ -0,0 +1,27 @@
+/*
+ * profile_helpers.h -- Function prototypes for profile helper functions
+ *
+ * Copyright (C) 2006 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+long profile_get_values
+	(profile_t profile, const char *const *names, char ***ret_values);
+
+void profile_free_list
+	(char **list);
+
+long profile_get_relation_names
+	(profile_t profile, const char **names, char ***ret_names);
+
+long profile_get_subsection_names
+	(profile_t profile, const char **names, char ***ret_names);
+
+void profile_release_string (char *str);
+
+long profile_init_path
+	(const char * filelist, profile_t *ret_profile);
diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
index 1ca6a2d..1adb4bf 100644
--- a/mkfs/xfs_mkfs.c
+++ b/mkfs/xfs_mkfs.c
@@ -20,8 +20,14 @@
 #include <ctype.h>
 #include "xfs_multidisk.h"
 #include "libxcmd.h"
+#include "profile.h"
+#include "prof_err.h"
 
-
+#define MKFS_XFS_CONF_DIR	ROOT_SYSCONFDIR "/mkfs.xfs.d/"
+#define CONFIG_MAX_KEY		1024
+#define CONFIG_MAX_VALUE	PATH_MAX
+#define CONFIG_MAX_BUFFER	CONFIG_MAX_KEY + CONFIG_MAX_VALUE + 3
+#define PARAM_OPTS		"T:b:d:i:l:L:m:n:KNp:qr:s:CfV"
 
 #define TERABYTES(count, blog)	((uint64_t)(count) << (40 - (blog)))
 #define GIGABYTES(count, blog)	((uint64_t)(count) << (30 - (blog)))
@@ -840,6 +846,43 @@
 };
 
 /*
+ * File configuration type settings
+ *
+ * These are the different possibilities by which you can end up parsing
+ * default settings with. DEFAULTS_BUILTIN indicates there was no configuration
+ * file parsed and we are using the built-in defaults on this code.
+ * DEFAULTS_CONFIG means the default configuration file was found and used.
+ * DEFAULTS_TYPE_CONFIG means the user asked for a custom configuration type
+ * and it was used, this means the default directory for mkfs.xfs
+ * configuration files was used to look for the type specified. If you need
+ * to override the default mkfs.xfs directory for configuration file you can
+ * use the MKFS_XFS_CONFIG environment variable to set the absolute path to
+ * your custom configuration file, when this is set the type is set to
+ * DEFAULTS_ENVIRONMENT_CONFIG.
+ */
+enum default_params_type {
+	DEFAULTS_BUILTIN = 0,
+	DEFAULTS_CONFIG,
+	DEFAULTS_ENVIRONMENT_CONFIG,
+	DEFAULTS_TYPE_CONFIG,
+};
+
+static const char *default_type_str(enum default_params_type type)
+{
+	switch (type) {
+	case DEFAULTS_BUILTIN:
+		return _("package build definitions");
+	case DEFAULTS_CONFIG:
+		return _("default configuration system file");
+	case DEFAULTS_ENVIRONMENT_CONFIG:
+		return _("custom configuration file set by environment");
+	case DEFAULTS_TYPE_CONFIG:
+		return _("custom configuration type file");
+	}
+	return _("Unkown\n");
+}
+
+/*
  * Default filesystem features and configuration values
  *
  * This structure contains the default mkfs values that are to be used when
@@ -848,7 +891,8 @@
  * calculations.
  */
 struct mkfs_default_params {
-	char	*source;	/* where the defaults came from */
+	enum default_params_type type; /* where the defaults came from */
+	const char *config_file;
 
 	int	sectorsize;
 	int	blocksize;
@@ -1414,6 +1458,12 @@
 	return 0;
 }
 
+static int block_config_parser(struct mkfs_default_params *dft, int subopt,
+			       bool value)
+{
+	return -EINVAL;
+}
+
 static int
 data_opts_parser(
 	struct opt_params	*opts,
@@ -1477,6 +1527,19 @@
 	return 0;
 }
 
+static int data_config_parser(struct mkfs_default_params *dft, int subopt,
+			      bool value)
+{
+	switch (subopt) {
+	case D_NOALIGN:
+		dft->sb_feat.nodalign = value;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
 static int
 inode_opts_parser(
 	struct opt_params	*opts,
@@ -1512,6 +1575,25 @@
 	return 0;
 }
 
+static int inode_config_parser(struct mkfs_default_params *dft, int subopt,
+			       bool value)
+{
+	switch (subopt) {
+	case I_ALIGN:
+		dft->sb_feat.inode_align = value;
+		break;
+	case I_PROJID32BIT:
+		dft->sb_feat.projid32bit = value;
+		break;
+	case I_SPINODES:
+		dft->sb_feat.spinodes = value;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
 static int
 log_opts_parser(
 	struct opt_params	*opts,
@@ -1558,6 +1640,19 @@
 	return 0;
 }
 
+static int log_config_parser(struct mkfs_default_params *dft, int subopt,
+			     bool value)
+{
+	switch (subopt) {
+	case L_LAZYSBCNTR:
+		dft->sb_feat.lazy_sb_counters = value;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
 static int
 meta_opts_parser(
 	struct opt_params	*opts,
@@ -1592,6 +1687,30 @@
 	return 0;
 }
 
+static int meta_config_parser(struct mkfs_default_params *dft, int subopt,
+			      bool value)
+{
+	switch (subopt) {
+	case M_CRC:
+		dft->sb_feat.crcs_enabled = value;
+		if (dft->sb_feat.crcs_enabled)
+			dft->sb_feat.dirftype = true;
+		break;
+	case M_FINOBT:
+		dft->sb_feat.finobt = value;
+		break;
+	case M_RMAPBT:
+		dft->sb_feat.rmapbt = value;
+		break;
+	case M_REFLINK:
+		dft->sb_feat.reflink = value;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
 static int
 naming_opts_parser(
 	struct opt_params	*opts,
@@ -1621,6 +1740,19 @@
 	return 0;
 }
 
+static int naming_config_parser(struct mkfs_default_params *dft, int subopt,
+				bool value)
+{
+	switch (subopt) {
+	case N_FTYPE:
+		dft->sb_feat.dirftype = value;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
 static int
 rtdev_opts_parser(
 	struct opt_params	*opts,
@@ -1651,6 +1783,19 @@
 	return 0;
 }
 
+static int rtdev_config_parser(struct mkfs_default_params *dft, int subopt,
+			       bool value)
+{
+	switch (subopt) {
+	case R_NOALIGN:
+		dft->sb_feat.nortalign = value;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
 static int
 sector_opts_parser(
 	struct opt_params	*opts,
@@ -1670,19 +1815,27 @@
 	return 0;
 }
 
+static int sector_config_parser(struct mkfs_default_params *dft, int subopt,
+				bool value)
+{
+	return -EINVAL;
+}
+
 struct subopts {
 	char		opt;
 	struct opt_params *opts;
 	int		(*parser)();
+	int		(*config_parser)(struct mkfs_default_params *dft,
+					 int subop, bool value);
 } subopt_tab[] = {
-	{ 'b', &bopts, block_opts_parser },
-	{ 'd', &dopts, data_opts_parser },
-	{ 'i', &iopts, inode_opts_parser },
-	{ 'l', &lopts, log_opts_parser },
-	{ 'm', &mopts, meta_opts_parser },
-	{ 'n', &nopts, naming_opts_parser },
-	{ 'r', &ropts, rtdev_opts_parser },
-	{ 's', &sopts, sector_opts_parser },
+	{ 'b', &bopts, block_opts_parser, block_config_parser },
+	{ 'd', &dopts, data_opts_parser, data_config_parser },
+	{ 'i', &iopts, inode_opts_parser, inode_config_parser },
+	{ 'l', &lopts, log_opts_parser, log_config_parser },
+	{ 'm', &mopts, meta_opts_parser, meta_config_parser },
+	{ 'n', &nopts, naming_opts_parser, naming_config_parser },
+	{ 'r', &ropts, rtdev_opts_parser, rtdev_config_parser },
+	{ 's', &sopts, sector_opts_parser, sector_config_parser },
 	{ '\0', NULL, NULL },
 };
 
@@ -1720,6 +1873,214 @@
 	}
 }
 
+static int
+parse_config_subopts(
+	char		opt,
+	bool            value,
+	char 		*line,
+	struct mkfs_default_params *dft)
+{
+	struct subopts	*sop = &subopt_tab[0];
+	char	**subopts = (char **)sop->opts->subopts;
+	int	subopt;
+	char *val;
+
+	while (sop->opts) {
+		if (sop->opt == opt)
+			break;
+		sop++;
+	}
+
+	/* should never happen */
+	if (!sop->opts)
+		return -EINVAL;
+
+	subopts = (char **)sop->opts->subopts;
+	subopt = getsubopt(&line, subopts, &val);
+	return (sop->config_parser)(dft, subopt, value);
+}
+
+static int get_subopt_type(const char *buffer)
+{
+	if (strncmp("block", buffer, 5) == 0)
+		return 'b';
+	if (strncmp("data", buffer, 4) == 0)
+		return 'd';
+	if (strncmp("inode", buffer, 5) == 0)
+		return 'i';
+	if (strncmp("log", buffer, 3) == 0)
+		return 'l';
+	if (strncmp("metadata", buffer, 8) == 0)
+		return 'm';
+	if (strncmp("naming", buffer, 6) == 0)
+		return 'n';
+	if (strncmp("rtdev", buffer, 5) == 0)
+		return 'r';
+	if (strncmp("sector", buffer, 6) == 0)
+		return 'n';
+	return 0;
+}
+
+static int mkfs_check_config_file_limits(const char *filename,
+					 const struct stat *sb,
+					 unsigned int max_entries)
+{
+	unsigned long long size_limit;
+
+	size_limit = CONFIG_MAX_BUFFER * max_entries;
+
+	/*
+	 * Detect wrap around -- if max_entries * size_limit goves over
+	 * unsigned long long we detect that there. Also libiniconfig only
+	 * groks max INT_MAX entries anyway.
+	 */
+	if (size_limit < max_entries || max_entries > INT_MAX)
+		return -E2BIG;
+
+	if (sb->st_size > size_limit) {
+		fprintf(stderr,
+			"File %s is too big! File size limit: %llu\n",
+			filename, size_limit);
+		return -E2BIG;
+	}
+
+	return 0;
+}
+
+static int parse_config_init(struct mkfs_default_params *dft)
+{
+	int ret;
+	profile_t profile;
+	struct stat _sp;
+	struct stat *sp = &_sp;
+	unsigned int num_subopts_sections = sizeof(subopt_tab) /
+					    sizeof(struct subopts) - 1;
+	char **sections = NULL, **attrs = NULL;
+	char **section = NULL, **attr = NULL;
+	const char *names[4];
+	int i, x, size = 0, num_attrs = 0;
+	int type = 0;
+	const char *config_fn[] = { dft->config_file, 0 };
+
+	names[0] = NULL;
+	names[1] = NULL;
+	names[2] = NULL;
+	names[3] = 0;
+
+	initialize_prof_error_table();
+
+	ret = profile_init(config_fn, &profile);
+	if (ret) {
+		if (dft->type != DEFAULTS_BUILTIN)
+			fprintf(stderr, _("ini_config_file_open(): could not open config file: %s: %s\n"),
+				dft->config_file, strerror(ret));
+		else
+			ret = 0;
+		goto out;
+	}
+
+	/* If we found a file magically, it would be the default file */
+	if (dft->type == DEFAULTS_BUILTIN)
+		dft->type = DEFAULTS_CONFIG;
+
+	ret = profile_get_stat(profile, sp);
+	if (!sp) {
+		ret = -EACCES;
+		fprintf(stderr, _("ini_config_get_stat(): could get stat on file: %s: %s\n"),
+			dft->config_file, strerror(ret));
+		goto out;
+	}
+
+	if (!sp->st_size)
+		return 0;
+
+	ret = mkfs_check_config_file_limits(dft->config_file, sp,
+					    num_subopts_sections);
+	if (ret)
+		goto out;
+
+	ret = profile_get_subsection_names(profile, names, &sections);
+	if (ret || sections == 0) {
+		fprintf(stderr, _("profile_get_subsection_names() failed: %s\n"),
+				error_message(ret));
+		goto out;
+	}
+
+	for (section = sections; *section; section++)
+		size++;
+
+	section = sections;
+
+        /*
+	 * We need to traverse backwards if we want to print out in the
+	 * same order as in the file. Its just the way the parser works.
+	 */
+	section = sections + size - 1;
+
+	for (i=0; i < size && *section; i++) {
+		names[0] = *section;
+
+		type = get_subopt_type(*section);
+		if (!type) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+		ret = profile_get_relation_names(profile, names, &attrs);
+		if (ret || attrs == 0) {
+			fprintf(stderr, _("profile_get_subsection_names() failed: %s\n"),
+					error_message(ret));
+			goto out;
+		}
+
+		for (attr = attrs; *attr; attr++)
+			num_attrs++;
+
+		attr = attrs + num_attrs - 1;
+
+		for (x=0; x < num_attrs; x++) {
+			unsigned int val;
+			char line[160];
+			bool value;
+
+			/*
+			 * Needed for when we're parsing last empty element.
+			 * If traversing from the end this will be our first.
+			 * Its just the way the parser works...
+			 */
+			if (*attr == 0 || !*attr) {
+				attr--;
+				continue;
+			}
+
+			memset(line, 0, sizeof(line));
+
+			ret = profile_get_uint(profile, *section, *attr, 0, 0, &val);
+			if (ret) {
+				fprintf(stderr, _("profile_get_subsection_names() failed: %s\n"),
+						error_message(ret));
+				goto out;
+			}
+
+			value = !!val;
+			snprintf(line, sizeof(line), "%s=%u", *attr, value);
+
+			ret = parse_config_subopts(type, value, line, dft);
+			if (ret) {
+				fprintf(stderr, _("error parsing line: %s\n"),
+						line);
+				goto out;
+			}
+			attr--;
+		}
+		section--;
+	}
+out:
+	if (profile)
+		profile_release(profile);
+	return ret;
+}
+
 static void
 validate_sectorsize(
 	struct mkfs_params	*cfg,
@@ -3177,11 +3538,13 @@
 	struct mkfs_params	*cfg,
 	char			*dfile,
 	char			*logfile,
-	char			*rtfile)
+	char			*rtfile,
+	struct mkfs_default_params *dft)
 {
 	struct sb_feat_args	*fp = &cfg->sb_feat;
 
 	printf(_(
+"config-file=%-22s\n"
 "meta-data=%-22s isize=%-6d agcount=%lld, agsize=%lld blks\n"
 "         =%-22s sectsz=%-5u attr=%u, projid32bit=%u\n"
 "         =%-22s crc=%-8u finobt=%u, sparse=%u, rmapbt=%u, reflink=%u\n"
@@ -3191,6 +3554,7 @@
 "log      =%-22s bsize=%-6d blocks=%lld, version=%d\n"
 "         =%-22s sectsz=%-5u sunit=%d blks, lazy-count=%d\n"
 "realtime =%-22s extsz=%-6d blocks=%lld, rtextents=%lld\n"),
+		dft->type != DEFAULTS_BUILTIN ? dft->config_file : "empty",
 		dfile, cfg->inodesize, (long long)cfg->agcount,
 			(long long)cfg->agsize,
 		"", cfg->sectorsize, fp->attr_version, fp->projid32bit,
@@ -3797,7 +4161,8 @@
 
 	/* build time defaults */
 	struct mkfs_default_params	dft = {
-		.source = _("package build definitions"),
+		.type = DEFAULTS_BUILTIN,
+		.config_file = MKFS_XFS_CONF_DIR "default",
 		.sectorsize = XFS_MIN_SECTORSIZE,
 		.blocksize = 1 << XFS_DFL_BLOCKSIZE_LOG,
 		.sb_feat = {
@@ -3819,6 +4184,9 @@
 			.nortalign = false,
 		},
 	};
+	char *tmp_config;
+	char custom_config[PATH_MAX];
+	int ret;
 
 	platform_uuid_generate(&cli.uuid);
 	progname = basename(argv[0]);
@@ -3827,25 +4195,47 @@
 	textdomain(PACKAGE);
 
 	/*
-	 * TODO: Sourcing defaults from a config file
-	 *
 	 * Before anything else, see if there's a config file with different
-	 * defaults. If a file exists in <package location>, read in the new
+	 * defaults. If a file exists in MKFS_XFS_CONF_DIR/default, read the new
 	 * default values and overwrite them in the &dft structure. This way the
 	 * new defaults will apply before we parse the CLI, and the CLI will
 	 * still be able to override them. When more than one source is
 	 * implemented, emit a message to indicate where the defaults being
 	 * used came from.
-	 *
-	 * printf(_("Default configuration sourced from %s\n"), dft.source);
 	 */
+	tmp_config = getenv("MKFS_XFS_CONFIG");
+	if (tmp_config != NULL) {
+		dft.config_file = tmp_config;
+		dft.type = DEFAULTS_ENVIRONMENT_CONFIG;
+	}
+
+	c = getopt(argc, argv, PARAM_OPTS);
+	if (c != EOF && c == 't') {
+		memset(custom_config, 0, sizeof(custom_config));
+		snprintf(custom_config, sizeof(custom_config), "%s%s",
+			 MKFS_XFS_CONF_DIR, optarg);
+		dft.config_file = custom_config;
+		dft.type = DEFAULTS_TYPE_CONFIG;
+	}
+
+	ret = parse_config_init(&dft);
+
+	printf(_("Default configuration sourced from %s\n"),
+	       default_type_str(dft.type));
+
+	if (ret && dft.type != DEFAULTS_BUILTIN)
+		usage();
 
 	/* copy new defaults into CLI parsing structure */
 	memcpy(&cli.sb_feat, &dft.sb_feat, sizeof(cli.sb_feat));
 	memcpy(&cli.fsx, &dft.fsx, sizeof(cli.fsx));
 
-	while ((c = getopt(argc, argv, "b:d:i:l:L:m:n:KNp:qr:s:CfV")) != EOF) {
+	while (true) {
+		if (c == EOF)
+			break;
 		switch (c) {
+		case 'T':
+			break;
 		case 'C':
 		case 'f':
 			force_overwrite = 1;
@@ -3885,6 +4275,7 @@
 		case '?':
 			unknown(optopt, "");
 		}
+		c = getopt(argc, argv, PARAM_OPTS);
 	}
 	if (argc - optind > 1) {
 		fprintf(stderr, _("extra arguments\n"));
@@ -3968,7 +4359,7 @@
 	calculate_log_size(&cfg, &cli, mp);
 
 	if (!quiet || dry_run) {
-		print_mkfs_cfg(&cfg, dfile, logfile, rtfile);
+		print_mkfs_cfg(&cfg, dfile, logfile, rtfile, &dft);
 		if (dry_run)
 			exit(0);
 	}