Hello,

Extensive travel in recent months allowed me to finish this code.

Here is the updated design:
https://fedorahosted.org/sssd/wiki/DesignDocs/ding-libs/INIConfigMerge

Patches:
0001 - Fix in case the Ref array is empty and we need to print/debug it
0002 - Declaration of the new function to do access checks
0003 - Big patch with core functionality
0004 - Updated access check code to use new internal access control function
0005 - File with expected output for unit test validation
0006 - Makefile and related changes to start building new code

No rush, take your time. :-)

--
Thank you,
Dmitri Pal

Sr. Engineering Manager IdM portfolio
Red Hat, Inc.

>From 9ce3ef526118ee8cf5e5dd8d28ddce971bba606e Mon Sep 17 00:00:00 2001
From: Dmitri Pal <d...@dpal.csb>
Date: Sun, 7 Sep 2014 22:55:02 +0200
Subject: [PATCH 6/6] [INI] Make the merge function build

---
 Makefile.am           |   15 +++++-
 ini/ini_configobj.h   |  115 +++++++++++++++++++++++++++++++++++++++++++++++++
 ini/libini_config.sym |    1 +
 3 files changed, 128 insertions(+), 3 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 32fcfae..7f38e50 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -255,6 +255,7 @@ libini_config_la_SOURCES = \
     ini/ini_get_valueobj.c \
     ini/ini_get_array_valueobj.c \
     ini/ini_list_valueobj.c \
+    ini/ini_augment.c \
     trace/trace.h
 libini_config_la_DEPENDENCIES = ini/libini_config.sym
 libini_config_la_LIBADD = \
@@ -286,19 +287,23 @@ dist_noinst_DATA += \
     ini/ini.d/real32be.conf \
     ini/ini.d/real32le.conf \
     ini/ini.d/symbols.conf \
-    ini/ini.d/new_line.conf
+    ini/ini.d/new_line.conf \
+    ini/ini.d/merge.validator
 
 check_PROGRAMS += \
     ini_config_ut \
     ini_comment_ut \
     ini_valueobj_ut \
-    ini_parse_ut
+    ini_parse_ut \
+    ini_augment_ut
 
 TESTS += \
     ini_config_ut \
     ini_comment_ut \
     ini_valueobj_ut \
-    ini_parse_ut
+    ini_parse_ut \
+    ini_augment_ut
+
 
 ini_config_ut_SOURCES = ini/ini_config_ut.c
 ini_config_ut_LDADD = \
@@ -314,6 +319,9 @@ ini_valueobj_ut_LDADD = libini_config.la libbasicobjects.la
 ini_parse_ut_SOURCES = ini/ini_parse_ut.c
 ini_parse_ut_LDADD = libini_config.la libcollection.la libbasicobjects.la
 
+ini_augment_ut_SOURCES = ini/ini_augment_ut.c
+ini_augment_ut_LDADD = libini_config.la libcollection.la libbasicobjects.la
+
 ini_config-docs:
 if HAVE_DOXYGEN
 	cd ini; \
@@ -324,6 +332,7 @@ clean-local-ini_config:
 	rm -f ./*.out
 	rm -f test.ini
 	rm -f ./foo.conf ./bom* #From ini_parse_ut
+	rm -f ./merge.validator.in #From ini_augment_ut
 
 ##############################################################################
 # Additional rules
diff --git a/ini/ini_configobj.h b/ini/ini_configobj.h
index 4ef98dc..620b545 100644
--- a/ini/ini_configobj.h
+++ b/ini/ini_configobj.h
@@ -374,6 +374,46 @@ enum INI_GET {
  * @}
  */
 
+
+/**
+ * @defgroup augment Constants and structures related to augmentation.
+ *
+ * @{
+ */
+
+/** Structure to pass access check parameters to augmentation function.
+ *
+ * flags            Define what to check.
+ *                  One can check file
+ *                  permissions with mask,
+ *                  uid, and gid of the file.
+ * uid              Expected uid of the file.
+ * gid              Expected gid of the file.
+ * mode             Expected mode of the file.
+ * mask             Mask to use in the mode check.
+ *                  Mask is always adjusted to
+ *                  include at least S_IRWXU,
+ *                  S_IRWXG and S_IRWXO
+ */
+struct access_check {
+    uint32_t flags;
+    uid_t uid;
+    gid_t gid;
+    mode_t mode;
+    mode_t mask;
+};
+
+/** Enumeration of augmentation modes. */
+enum augmode {
+    INI_AUG_ANY   = 0, /**< Allow any augmentation. */
+    INI_AUG_ADD   = 1, /**< Allow only new sections. */
+    INI_AUG_OVER  = 2  /**< Allow section updates. */
+};
+
+/**
+ * @}
+ */
+
 /**
  * @brief Name of the default section.
  *
@@ -780,6 +820,81 @@ int ini_config_merge(struct ini_cfgobj *first,
                      uint32_t collision_flags,
                      struct ini_cfgobj **result);
 
+
+/**
+ * @brief Augment configuration
+ *
+ * Function merges the main configuration file 
+ * with the configuration file snippets 
+ * read from a specified directory.
+ *
+ * @param[in]  base_cfg         A configuration object
+ *                              that will be augmented.
+ * @param[in]  path             Path to a directory where
+ *                              configuration snippets
+ *                              will be read from.
+ * @param[in]  patterns         List of regular expressions
+ *                              that the name of a snippet file
+ *                              has to match to be considered
+ *                              for merge.
+ * @param[in]  sections         List of regular expressions
+ *                              that the section names in the snippet
+ *                              file need to match. If file contains
+ *                              sections that do not match any patterns
+ *                              the file is skipped and error is recorded.
+ * @param[in]  check_perm       Pointer to structure that
+ *                              holds criteria for the 
+ *                              access check.
+ * @param[in]  error_level      Flags that control actions
+ *                              in case of parsing error in a snippet file.
+ * @param[in]  collision_flags  These flags control how the potential
+ *                              collisions between keys and sections
+ *                              within the snippet file will be handled.
+ *                              For more information
+ *                              see collision flag definitions.
+ * @param[in]  parse_flags      Flags that control parsing process,
+ *                              for example how to handle spaces at
+ *                              the beginning of the line.
+ * @param[in]  merge_flags      Flags that control handling
+ *                              of the duplicate sections or keys
+ *                              during merging of the snippets.
+ *                              They are different from the collision flags
+ *                              because duplicate sections and keys inside
+ *                              are snippets most likely will be handled as
+ *                              'last value wins' while during merge
+ *                              the attempt to overwrite
+ *                              a specific section might be treated as
+ *                              an error.
+ * @param[out] result_cfg       A new configuration object,
+ *                              the result of the merge.
+ * @param[out] error_list       List of strings that
+ *                              contains all encountered
+ *                              errors.
+ *                              It can be NULL, in this case list of errors
+ *                              is not populated.
+ * @param[out] success_list     List of strings that
+ *                              contains file names of snippets that were
+ *                              successfully merged.
+ *                              It can be NULL, in this case list of files
+ *                              is not populated.
+ *
+ * @return 0 - Success.
+ * @return EINVAL - Invalid parameter.
+ * @return ENOMEM - No memory.
+ */
+int ini_config_augment(struct ini_cfgobj *base_cfg,
+                       const char *path,
+                       const char **patterns,
+                       const char **sections,
+                       struct access_check *check_perm,
+                       int error_level,
+                       uint32_t collision_flags,
+                       uint32_t parse_flags,
+                       uint32_t merge_flags,
+                       struct ini_cfgobj **result_cfg,
+                       struct ref_array **error_list,
+                       struct ref_array **success_list);
+
 /**
  * @brief Set the folding boundary
  *
diff --git a/ini/libini_config.sym b/ini/libini_config.sym
index 3ca15bb..33d2246 100644
--- a/ini/libini_config.sym
+++ b/ini/libini_config.sym
@@ -62,6 +62,7 @@ global:
     ini_config_parse;
     ini_config_copy;
     ini_config_merge;
+    ini_config_augment;
     ini_config_set_wrap;
     ini_config_serialize;
     ini_get_section_list;
-- 
1.7.1

>From 9f80182fecbf1c1800a22ff279bf8915f4196f42 Mon Sep 17 00:00:00 2001
From: Dmitri Pal <d...@dpal.csb>
Date: Sun, 7 Sep 2014 16:39:58 +0200
Subject: [PATCH 2/6] [INI] Declaring new internal access check function

---
 ini/ini_config_priv.h |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/ini/ini_config_priv.h b/ini/ini_config_priv.h
index e1292f7..331363f 100644
--- a/ini/ini_config_priv.h
+++ b/ini/ini_config_priv.h
@@ -99,6 +99,13 @@ int valid_collision_flags(uint32_t collision_flags);
 /* Empty section */
 int empty_section(struct collection_item *sec);
 
+/* Internal access check function */
+int access_check_int(struct stat *file_stats,
+                     uint32_t flags,
+                     uid_t uid,
+                     gid_t gid,
+                     mode_t mode,
+                     mode_t mask);
 
 
 #endif
-- 
1.7.1

>From 09a23b41da6f7a4584f652c4a5a95bb158b0dfe2 Mon Sep 17 00:00:00 2001
From: Dmitri Pal <d...@dpal.csb>
Date: Sun, 7 Sep 2014 16:42:00 +0200
Subject: [PATCH 3/6] [INI] New function to merge snippets

The patch includes the implementation of the function
and the unit test
---
 ini/ini_augment.c    |  924 ++++++++++++++++++++++++++++++++++++++++++++++++++
 ini/ini_augment_ut.c |  353 +++++++++++++++++++
 2 files changed, 1277 insertions(+), 0 deletions(-)
 create mode 100644 ini/ini_augment.c
 create mode 100644 ini/ini_augment_ut.c

diff --git a/ini/ini_augment.c b/ini/ini_augment.c
new file mode 100644
index 0000000..7f59d38
--- /dev/null
+++ b/ini/ini_augment.c
@@ -0,0 +1,924 @@
+/*
+    INI LIBRARY
+
+    Module represents part of the INI interface.
+    The main function in this module allows to merge
+    snippets of different config files.
+
+    Copyright (C) Dmitri Pal <d...@redhat.com> 2014
+
+    INI Library is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    INI Library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with INI Library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define _GNU_SOURCE
+#include "config.h"
+#include <errno.h>
+#include <stdarg.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <regex.h>
+#include "trace.h"
+#include "collection.h"
+#include "collection_tools.h"
+#include "ini_configobj.h"
+#include "ini_config_priv.h"
+#include "ini_defines.h"
+#include "path_utils.h"
+
+/* Constants to match */
+#define INI_CURRENT_DIR "."
+#define INI_PARENT_DIR ".."
+
+/* Size of incremental growth for ref of the array of strings */
+#define INI_AUG_ARR_SIZE_INC 50
+
+
+/* Function to add an error to the array */
+static void ini_aug_add_string(struct ref_array *ra,
+                               const char *format,
+                               ...)
+{
+    va_list args;
+    char *result = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    va_start(args, format);
+
+    if(vasprintf(&result, format, args )) {
+        TRACE_INFO_STRING("String:", result);
+        ref_array_append(ra, (void *)&result);
+    }
+
+    va_end(args);
+
+    TRACE_FLOW_EXIT();
+}
+
+/* Add error about opening directory */
+static void add_dir_open_error(int error, char *dirname,
+                               struct ref_array *ra_err)
+{
+
+    TRACE_FLOW_ENTRY();
+
+    switch(error) {
+    case EACCES:
+        ini_aug_add_string(ra_err,
+                           "Permission denied opening %s.",
+                           dirname);
+        break;
+    case EMFILE:
+    case ENFILE:
+        ini_aug_add_string(ra_err,
+                           "Too many file descriptors in use while opening %s.",
+                           dirname);
+        break;
+    case ENOENT:
+        ini_aug_add_string(ra_err,
+                           "Directory %s does not exist.",
+                           dirname);
+        break;
+    case ENOTDIR:
+        ini_aug_add_string(ra_err,
+                           "Path %s is not a directory.",
+                           dirname);
+        break;
+    case ENOMEM:
+        ini_aug_add_string(ra_err,
+                           "Insufficient memory while opening %s.",
+                           dirname);
+        break;
+    default:
+        ini_aug_add_string(ra_err,
+                           "Unknown error while opening %s.",
+                           dirname);
+        break;
+    }
+
+    TRACE_FLOW_EXIT();
+}
+
+/* Cleanup callback for regex array */
+static void regex_cleanup(void *elem,
+                          ref_array_del_enum type,
+                          void *data)
+{
+    TRACE_FLOW_ENTRY();
+    regfree(*((regex_t **)elem));
+    free(*((regex_t **)elem));
+    TRACE_FLOW_EXIT();
+}
+
+
+/* Prepare array of regular expressions */
+static int ini_aug_regex_prepare(const char **patterns,
+                                 struct ref_array *ra_err,
+                                 struct ref_array **ra_regex)
+{
+    int error = EOK;
+    int reg_err = 0;
+    char **pat = NULL;
+    struct ref_array *ra = NULL;
+    regex_t *preg = NULL;
+    size_t buf_size = 0;
+    char *err_str = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    if (patterns) {
+
+        /* Create array to mark bad patterns */
+        error = ref_array_create(&ra,
+                                 sizeof(regex_t *),
+                                 INI_AUG_ARR_SIZE_INC,
+                                 regex_cleanup,
+                                 NULL);
+        if (error) {
+            TRACE_ERROR_NUMBER("Failed to create array.", error);
+            return error;
+        }
+
+        memcpy(&pat, &patterns, sizeof(char **));
+
+        /* Run through the list and save precompiled patterns */
+        while (*pat) {
+            TRACE_INFO_STRING("Pattern:", *pat);
+
+            preg = calloc(1, sizeof(regex_t));
+            if (preg == NULL) {
+                TRACE_ERROR_NUMBER("Failed to create array.", ENOMEM);
+                ref_array_destroy(ra);
+                return ENOMEM;
+            }
+            reg_err = regcomp(preg, *pat, REG_NOSUB);
+            if (reg_err) {
+                /* Get size, allocate buffer, record error... */
+                buf_size = regerror(reg_err, preg, NULL, 0);
+                err_str = malloc (buf_size);
+                if (err_str == NULL) {
+                    TRACE_ERROR_NUMBER("Failed to create array.", ENOMEM);
+                    ref_array_destroy(ra);
+                    free(preg);
+                    return ENOMEM;
+                }
+                regerror(reg_err, preg, err_str, buf_size);
+                free(preg);
+                ini_aug_add_string(ra_err,
+                                   "Failed to process expression: %s."
+                                   " Compilation returned error: %s",
+                                   *pat, err_str);
+
+                /* All error processing is done - advance to next pattern */
+                pat++;
+                continue;
+            }
+            /* In case of no error add compiled expression into the buffer */
+            error = ref_array_append(ra, (void *)&preg);
+            if (error) {
+                TRACE_ERROR_NUMBER("Failed to add element to array.", error);
+                ref_array_destroy(ra);
+                free(preg);
+                return error;
+            }
+            /* Advance */
+            pat++;
+        }
+    }
+
+    *ra_regex = ra;
+    /* ref_array_debug(*ra_regex, 1); */
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+/* Match file name */
+static bool ini_aug_match_name(char *name,
+                               struct ref_array *ra_regex)
+{
+    uint32_t len = 0;
+    uint32_t i = 0;
+    bool match = false;
+    regex_t *preg = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    len = ref_array_len(ra_regex);
+    if (len == 0) {
+        /* List is empty - nothing to do */
+        TRACE_FLOW_EXIT();
+        return true;
+    }
+
+    TRACE_INFO_STRING("Name to match:", name);
+    TRACE_INFO_NUMBER("Number of regexes:", len);
+    /* ref_array_debug(ra_regex, 1);*/
+
+    for (i = 0; i < len; i++) {
+        preg = *((regex_t **)ref_array_get(ra_regex, i, NULL));
+        if (regexec(preg, name, 0, NULL, 0) == 0) {
+            TRACE_INFO_NUMBER("Name matched regex number:", i);
+            match = true;
+            break;
+        }
+    }
+
+    TRACE_FLOW_EXIT();
+    return match;
+}
+
+/* Check if this is a file and validate permission */
+static bool ini_check_file_perm(char *name,
+                                struct access_check *check_perm,
+                                struct ref_array *ra_err)
+{
+    bool ret = false;
+    int error = EOK;
+    struct stat file_info;
+
+    TRACE_FLOW_ENTRY();
+
+    errno = 0;
+    if (stat(name, &file_info) == -1) {
+        error = errno;
+        TRACE_ERROR_NUMBER("Failed to get file stats", error);
+        ini_aug_add_string(ra_err,
+                           "Failed to read metadata for file %s."
+                           " Skipping.",
+                           name);
+        return false;
+    }
+
+    if (!S_ISREG(file_info.st_mode)) {
+        ini_aug_add_string(ra_err,
+                           "File %s is not a regular file. Skipping.",
+                           name);
+        return false;
+    }
+
+    if ((check_perm) && (check_perm->flags)) {
+        error = access_check_int(&file_info,
+                                 check_perm->flags,
+                                 check_perm->uid,
+                                 check_perm->gid,
+                                 check_perm->mode,
+                                 check_perm->mask);
+        if(error) {
+            TRACE_ERROR_NUMBER("Access check returned", error);
+            ini_aug_add_string(ra_err,
+                               "File %s did not pass access check. Skipping.",
+                               name);
+            return false;
+        }
+    }
+
+    ret = true;
+
+    TRACE_INFO_STRING("Returning", (ret ? "true" : "false"));
+    TRACE_FLOW_EXIT();
+
+    return ret;
+}
+
+/* Sort array */
+static void ini_aug_sort_list(struct ref_array *ra_list)
+{
+    unsigned len = 0, i = 0, j = 0, k = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    len = ref_array_len(ra_list);
+    if (len == 0) return;
+
+    /* If have trace output array before sorting */
+/*
+#ifdef HAVE_TRACE
+    for (i = 0; i < len; i++) {
+        TRACE_INFO_STRING("Before:",
+                          *((char **) ref_array_get(ra_list, i, NULL)));
+
+    }
+#endif
+*/
+
+    for (k = 0; k < len-1; k++) {
+        i = k;
+        j = k + 1;
+
+        while ((j > 0) && (strncmp(*((char **) ref_array_get(ra_list, i, NULL)),
+                                   *((char **) ref_array_get(ra_list, j, NULL)),
+                                   PATH_MAX) > 0)) {
+            ref_array_swap(ra_list, i, j);
+            i--;
+            j--;
+        }
+    }
+
+    /* And after sorting */
+/*
+#ifdef HAVE_TRACE
+    for (i = 0; i < len; i++) {
+        TRACE_INFO_STRING("After:",
+                          *((char **) ref_array_get(ra_list, i, NULL)));
+
+    }
+#endif
+*/
+
+    TRACE_FLOW_EXIT();
+}
+
+/* Construct snippet lists based on the directory */
+static int ini_aug_construct_list(char *dirname ,
+                                  const char **patterns,
+                                  struct access_check *check_perm,
+                                  struct ref_array *ra_list,
+                                  struct ref_array *ra_err)
+{
+
+    int error = EOK;
+    DIR *dir = NULL;
+    struct dirent *entry = NULL;
+    char *snipname = NULL;
+    char fullname[PATH_MAX + 1] = {0};
+    struct ref_array *ra_regex = NULL;
+    bool match = false;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Prepare patterns */
+    error = ini_aug_regex_prepare(patterns,
+                                  ra_err,
+                                  &ra_regex);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to prepare regex array.", error);
+        return error;
+    }
+
+    /* Open directory */
+    errno = 0;
+    dir = opendir(dirname);
+    if (!dir) {
+        error = errno;
+        if (error == ENOMEM) {
+            TRACE_ERROR_NUMBER("No memory to open dir.", ENOMEM);
+            ref_array_destroy(ra_regex);
+            return ENOMEM;
+        }
+        /* Log an error, it is a recoverable error */
+        add_dir_open_error(error, dirname, ra_err);
+        return EOK;
+    }
+
+    /* Loop through the directory */
+    while ((entry = readdir(dir)) != NULL)
+    {
+        TRACE_INFO_STRING("Processing", entry->d_name);
+
+        /* Always skip current and parent dirs */
+        if ((strncmp(entry->d_name,
+                     INI_CURRENT_DIR,
+                     sizeof(INI_CURRENT_DIR)) == 0) ||
+            (strncmp(entry->d_name,
+                     INI_PARENT_DIR,
+                     sizeof(INI_PARENT_DIR)) == 0)) continue;
+
+        /* Match names */
+        match = ini_aug_match_name(entry->d_name, ra_regex);
+
+        if (match) {
+
+            snprintf(fullname, PATH_MAX, "%s/%s", dirname, entry->d_name);
+
+            if(ini_check_file_perm(fullname, check_perm, ra_err)) {
+
+                /* Dup name and add to the array */
+                snipname = NULL;
+                snipname = strdup(fullname);
+                if (error) {
+                    TRACE_ERROR_NUMBER("Failed to dup string.", ENOMEM);
+                    ref_array_destroy(ra_regex);
+                    return ENOMEM;
+                }
+
+                error = ref_array_append(ra_list, (void *)&snipname);
+                if (error) {
+                    TRACE_ERROR_NUMBER("No memory to add file to "
+                                       "the snippet list.",
+                                       ENOMEM);
+                    ref_array_destroy(ra_regex);
+                    return ENOMEM;
+                }
+            }
+        }
+        else {
+            ini_aug_add_string(ra_err,
+                               "File %s did not match provided patterns."
+                               " Skipping.",
+                               entry->d_name);
+        }
+    }
+
+    closedir(dir);
+    ref_array_destroy(ra_regex);
+
+    ini_aug_sort_list(ra_list);
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+/* Construct the full dir path */
+static int ini_aug_expand_path(const char *path, char **fullname)
+{
+    int error = EOK;
+    char *dirname = NULL;
+
+    TRACE_FLOW_ENTRY();
+    TRACE_INFO_STRING("Input path", path);
+
+    dirname = malloc(PATH_MAX + 1);
+    if (!dirname) {
+        TRACE_ERROR_NUMBER("Failed to allocate memory for file path.", ENOMEM);
+        return ENOMEM;
+    }
+
+    /* Make the path */
+    error = make_normalized_absolute_path(dirname,
+                                          PATH_MAX,
+                                          path);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to resolve path", error);
+        free(dirname);
+        /* This is a recoverable error */
+        *fullname = NULL;
+    }
+    else *fullname = dirname;
+
+    TRACE_INFO_STRING("Output path", *fullname);
+    TRACE_FLOW_EXIT();
+
+    return EOK;
+}
+
+/* Prepare the lists of the files that need to be merged */
+static int ini_aug_preprare(const char *path,
+                            const char **patterns,
+                            struct access_check *check_perm,
+                            struct ref_array *ra_list,
+                            struct ref_array *ra_err)
+{
+    int error = EOK;
+    char *dirname = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Contruct path */
+    error = ini_aug_expand_path(path, &dirname);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to allocate memory for dir path.", error);
+        return error;
+    }
+
+    /* Was it a good path? */
+    if (!dirname) {
+        TRACE_ERROR_NUMBER("Failed to resolve path", error);
+        ini_aug_add_string(ra_err, "Could not resolve directory path %s.",
+                           path);
+        /* Path might not exist so it is a recoverable error */
+        return EOK;
+    }
+
+    /* Construct snipet lists */
+    error = ini_aug_construct_list(dirname,
+                                   patterns,
+                                   check_perm,
+                                   ra_list,
+                                   ra_err);
+    free(dirname);
+
+    TRACE_FLOW_EXIT();
+    return error;
+}
+
+/* Cleanup callback for string arrays */
+static void array_cleanup(void *elem,
+                          ref_array_del_enum type,
+                          void *data)
+{
+    TRACE_FLOW_ENTRY();
+    free(*((char **)elem));
+    TRACE_FLOW_EXIT();
+}
+
+/* Check that sections are in the given list */
+static int ini_aug_match_sec(struct ini_cfgobj *snip_cfg,
+                             struct ref_array *ra_regex,
+                             struct ref_array *ra_err,
+                             char *snip_name,
+                             bool *skip)
+{
+    int error = EOK;
+    char **section_list = NULL;
+    char **section_iter = NULL;
+    int size = 0;
+    bool match = false;
+    int match_count = 0;
+    int section_count = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    section_list = ini_get_section_list(snip_cfg, &size, &error);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed create section list", error);
+        return error;
+    }
+
+    if (section_list == NULL) {
+        /* No sections in the file */
+        ini_aug_add_string(ra_err, "No sections found in file %s. Skipping.",
+                           snip_name);
+        *skip = true;
+        TRACE_FLOW_EXIT();
+        return EOK;
+    }
+
+    section_iter = section_list;
+
+    while (*section_iter) {
+        match = ini_aug_match_name(*section_iter, ra_regex);
+        if (match) {
+            match_count++;
+            TRACE_INFO_STRING("Matched section", *section_iter);
+        }
+        else {
+            TRACE_INFO_STRING("Section not matched", *section_iter);
+            ini_aug_add_string(ra_err, "Section [%s] found in file %s is"
+                                       " not allowed.",
+                               *section_iter, snip_name);
+        }
+        section_count++;
+        section_iter++;
+    }
+
+    ini_free_section_list(section_list);
+
+    /* Just in case check that we processed anything */
+    if (section_count == 0) {
+        ini_aug_add_string(ra_err, "No sections found in file %s. Skipping.",
+                           snip_name);
+        *skip = true;
+        TRACE_FLOW_EXIT();
+        return EOK;
+    }
+
+    /* Were all sections matched? */
+    if (section_count != match_count) {
+        /* Snippet containes sections that are not allowed */
+        ini_aug_add_string(ra_err, "File %s contains sections that"
+                                   " are not allowed. Skipping.",
+                           snip_name);
+        *skip = true;
+        TRACE_FLOW_EXIT();
+        return EOK;
+    }
+
+    /* Everything matched OK so we give green light to merge */
+    TRACE_INFO_STRING("File will be included", snip_name);
+    *skip = false;
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+
+/* Apply snippets */
+static int ini_aug_apply(struct ini_cfgobj *cfg,
+                         struct ref_array *ra_list,
+                         const char **sections,
+                         int error_level,
+                         uint32_t collision_flags,
+                         uint32_t parse_flags,
+                         uint32_t merge_flags,
+                         struct ref_array *ra_err,
+                         struct ref_array *ra_ok,
+                         struct ini_cfgobj **out_cfg)
+{
+    int error = EOK;
+    uint32_t len = 0;
+    uint32_t i = 0;
+    uint32_t j = 0;
+    struct ini_cfgfile *file_ctx = NULL;
+    struct ini_cfgobj *snip_cfg = NULL;
+    struct ini_cfgobj *res_cfg = NULL;
+    struct ini_cfgobj *tmp_cfg = NULL;
+    char **error_list = NULL;
+    unsigned cnt = 0;
+    bool skip = false;
+    struct ref_array *ra_regex = NULL;
+    char *snip_name = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    len = ref_array_len(ra_list);
+    if (len == 0) {
+        /* List is empty - nothing to do */
+        *out_cfg = NULL;
+        TRACE_FLOW_EXIT();
+        return EOK;
+    }
+
+    error = ini_config_copy(cfg, &res_cfg);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to copy config object", error);
+        return error;
+    }
+
+    /* Prepare patterns */
+    error = ini_aug_regex_prepare(sections,
+                                  ra_err,
+                                  &ra_regex);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to prepare regex array.", error);
+        ini_config_destroy(res_cfg);
+        return error;
+    }
+
+    /* Loop through the snippets */
+    for (i = 0; i < len; i++) {
+
+        /* Prepare config object */
+        error = ini_config_create(&snip_cfg);
+        if (error) {
+            TRACE_ERROR_NUMBER("Failed to create config object", error);
+            ini_config_destroy(res_cfg);
+            ref_array_destroy(ra_regex);
+            return error;
+        }
+
+        /* Process snippet */
+        snip_name = *((char **)ref_array_get (ra_list, i, NULL));
+        TRACE_INFO_STRING("Processing", snip_name);
+
+        /* Open file */
+        error = ini_config_file_open(snip_name,
+                                     INI_META_NONE,
+                                     &file_ctx);
+        if (error) {
+            TRACE_ERROR_NUMBER("Failed to open snippet.", error);
+            ini_aug_add_string(ra_err, "Failed to open file %s.", snip_name);
+            ini_config_destroy(snip_cfg);
+            /* We can recover so go on */
+            continue;
+        }
+
+        TRACE_INFO_NUMBER("Error level:", error_level);
+        TRACE_INFO_NUMBER("Collision flags:", collision_flags);
+        TRACE_INFO_NUMBER("Parse level:", parse_flags);
+
+        /* Read config */
+        error = ini_config_parse(file_ctx,
+                                 error_level,
+                                 collision_flags,
+                                 parse_flags,
+                                 snip_cfg);
+
+        ini_config_file_destroy(file_ctx);
+        file_ctx = NULL;
+
+        if (error) {
+            TRACE_ERROR_NUMBER("Failed to parse configuration.", error);
+            cnt = ini_config_error_count(snip_cfg);
+            if (cnt) {
+                ini_aug_add_string(ra_err,
+                                   "Errors detected while parsing: %s.",
+                                   snip_name);
+
+                /* Extract errors */
+                error = ini_config_get_errors(snip_cfg, &error_list);
+                if (error) {
+                    TRACE_ERROR_NUMBER("Can't get errors.", error);
+                    ini_config_destroy(snip_cfg);
+                    ini_config_destroy(res_cfg);
+                    ref_array_destroy(ra_regex);
+                    return error;
+                }
+
+                /* Copy errors into error array */
+                for (j=0; j< cnt; j++) {
+                    ini_aug_add_string(ra_err, error_list[j]);
+                }
+                ini_config_free_errors(error_list);
+            }
+            /* The snippet was malformed, this is OK, go on */
+            if (error_level != INI_STOP_ON_NONE) {
+                ini_aug_add_string(ra_err,
+                                   "Due to errors file %s is not considered."
+                                   " Skipping.",
+                                   snip_name);
+                ini_config_destroy(snip_cfg);
+                continue;
+            }
+            /* If we are told to not stop try to process anyway */
+        }
+
+        /* Validate that file contains only allowed sections */
+        if (sections) {
+            /* Use a safe default, function should update it anyways
+             * but it is better to not merge than to allow bad snippet */
+            skip = true;
+            error = ini_aug_match_sec(snip_cfg, ra_regex, ra_err,
+                                      snip_name, &skip);
+            if (error) {
+                TRACE_ERROR_NUMBER("Failed to validate section.", error);
+                ini_config_destroy(snip_cfg);
+                ini_config_destroy(res_cfg);
+                ref_array_destroy(ra_regex);
+                return error;
+            }
+        }
+
+        /* Merge */
+        if (!skip) {
+            /* col_debug_collection(res_cfg->cfg, COL_TRAVERSE_DEFAULT); */
+            error = ini_config_merge(res_cfg, snip_cfg, merge_flags, &tmp_cfg);
+            if (error) {
+                if (error == ENOMEM) {
+                    TRACE_ERROR_NUMBER("Merge failed.", error);
+                    ini_config_destroy(snip_cfg);
+                    ini_config_destroy(res_cfg);
+                    ref_array_destroy(ra_regex);
+                    return error;
+                }
+                else if 
+                    ((error == EEXIST) &&
+                     ((((merge_flags & INI_MS_MASK) == INI_MS_DETECT) &&
+                       ((merge_flags & INI_MV2S_MASK) != INI_MV2S_ERROR)) ||
+                      (((merge_flags & INI_MS_MASK) != INI_MS_ERROR) &&
+                       ((merge_flags & INI_MV2S_MASK) == INI_MV2S_DETECT)))) {
+                        TRACE_ERROR_NUMBER("Got error in detect mode", error);
+                        /* Fall through! */
+                }
+                else {
+                    ini_aug_add_string(ra_err,
+                                       "Errors during merge."
+                                       " Snippet ignored %s.",
+                                       snip_name);
+                    /* The snippet failed to merge, this is OK, go on */
+                    TRACE_INFO_NUMBER("Merge failure.Continue. Error", error);
+                    ini_config_destroy(snip_cfg);
+                    continue;
+                }
+            }
+            TRACE_INFO_STRING("Merged file.", snip_name);
+            /* col_debug_collection(tmp_cfg->cfg, COL_TRAVERSE_DEFAULT); */
+            ini_config_destroy(res_cfg);
+            res_cfg = tmp_cfg;
+
+            /* Record that snippet was successfully merged */
+            ini_aug_add_string(ra_ok, "%s", snip_name);
+        }
+        /* Cleanup */
+        ini_config_destroy(snip_cfg);
+    }
+
+    ref_array_destroy(ra_regex);
+    *out_cfg = res_cfg;
+    TRACE_FLOW_EXIT();
+    return error;
+}
+
+/* Function to merge additional snippets of the config file
+ * from a provided directory.
+ */
+int ini_config_augment(struct ini_cfgobj *base_cfg,
+                       const char *path,
+                       const char **patterns,
+                       const char **sections,
+                       struct access_check *check_perm,
+                       int error_level,
+                       uint32_t collision_flags,
+                       uint32_t parse_flags,
+                       uint32_t merge_flags,
+                       struct ini_cfgobj **result_cfg,
+                       struct ref_array **error_list,
+                       struct ref_array **success_list)
+{
+    int error = EOK;
+    /* The internal list that will hold snippet file names */
+    struct ref_array *ra_list = NULL;
+    /* List of error strings that will be returned to the caller */
+    struct ref_array *ra_err = NULL;
+    /* List of files that were merged */
+    struct ref_array *ra_ok = NULL;
+    /* Resulting configuration object */
+    struct ini_cfgobj *out_cfg = NULL;
+
+    /* Check arguments */
+    if (base_cfg == NULL) {
+        TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
+        return EINVAL;
+    }
+
+    if (result_cfg == NULL) {
+        TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
+        return EINVAL;
+    }
+
+
+    /* Create arrays for lists */
+    if ((ref_array_create(&ra_list,
+                          sizeof(char *),
+                          INI_AUG_ARR_SIZE_INC,
+                          array_cleanup,
+                          NULL) != 0) ||
+        (ref_array_create(&ra_err,
+                          sizeof(char *),
+                          INI_AUG_ARR_SIZE_INC * 5,
+                          array_cleanup,
+                          NULL) != 0) ||
+        (ref_array_create(&ra_ok,
+                          sizeof(char *),
+                          INI_AUG_ARR_SIZE_INC * 5,
+                          array_cleanup,
+                          NULL) != 0)) {
+        TRACE_ERROR_NUMBER("Failed to allocate memory for arrays.",
+                           ENOMEM);
+        ref_array_destroy(ra_list);
+        ref_array_destroy(ra_err);
+        ref_array_destroy(ra_ok);
+        return ENOMEM;
+    }
+
+    /* Construct snipet lists */
+    error = ini_aug_preprare(path,
+                             patterns,
+                             check_perm,
+                             ra_list,
+                             ra_err);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to prepare lists of snippets.",
+                           error);
+        ref_array_destroy(ra_list);
+        ref_array_destroy(ra_err);
+        ref_array_destroy(ra_ok);
+        return error;
+    }
+
+    /* Apply snippets */
+    error = ini_aug_apply(base_cfg,
+                          ra_list,
+                          sections,
+                          error_level,
+                          collision_flags,
+                          parse_flags,
+                          merge_flags,
+                          ra_err,
+                          ra_ok,
+                          &out_cfg);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to process snippet list.",
+                           error);
+        ref_array_destroy(ra_list);
+        ref_array_destroy(ra_err);
+        ref_array_destroy(ra_ok);
+        return error;
+    }
+
+    /* Cleanup */
+    ref_array_destroy(ra_list);
+
+    *result_cfg = out_cfg;
+
+    if (error_list) {
+        *error_list = ra_err;
+    }
+    else {
+        ref_array_destroy(ra_err);
+    }
+
+    if (success_list) {
+        *success_list = ra_ok;
+    }
+    else {
+        ref_array_destroy(ra_ok);
+    }
+
+    TRACE_FLOW_EXIT();
+    return error;
+}
+
diff --git a/ini/ini_augment_ut.c b/ini/ini_augment_ut.c
new file mode 100644
index 0000000..94d610e
--- /dev/null
+++ b/ini/ini_augment_ut.c
@@ -0,0 +1,353 @@
+/*
+    INI LIBRARY
+
+    Unit test for the comment object.
+
+    Copyright (C) Dmitri Pal <d...@redhat.com> 2014
+
+    INI Library is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    INI Library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with INI Library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+/* #define TRACE_LEVEL 7 */
+#define TRACE_HOME
+#include "trace.h"
+#include "ini_configobj.h"
+#include "ini_config_priv.h"
+#include "collection_tools.h"
+#include "path_utils.h"
+
+int verbose = 0;
+
+#define INIOUT(foo) \
+    do { \
+        if (verbose) { printf("%30s(%4d): ",__FUNCTION__,__LINE__); foo; } \
+    } while(0)
+
+typedef int (*test_fn)(void);
+
+void print_list(struct ref_array *list);
+int print_list_to_file(struct ref_array *list,
+                       const char *filename,
+                       const char *mode);
+static int expand_path(const char *path, char **fullname);
+
+
+/* Construct the full dir path */
+static int expand_path(const char *path, char **fullname)
+{
+    int error = EOK;
+    char *dirname = NULL;
+
+    TRACE_FLOW_ENTRY();
+    TRACE_INFO_STRING("Input path", path);
+
+    dirname = malloc(PATH_MAX + 1);
+    if (!dirname) {
+        INIOUT(printf("Failed to allocate memory for file path."));
+        return ENOMEM;
+    }
+
+    /* Make the path */
+    error = make_normalized_absolute_path(dirname,
+                                          PATH_MAX,
+                                          path);
+    if (error) {
+        INIOUT(printf("Failed to resolve path %d\n", error));
+        free(dirname);
+        return error;
+    }
+    else *fullname = dirname;
+
+    TRACE_INFO_STRING("Output path", *fullname);
+    TRACE_FLOW_EXIT();
+
+    return EOK;
+}
+
+static int prepare_results(const char *srcdir,
+                           const char *srcfile,
+                           const char *destfile)
+{
+    int error = EOK;
+    char *exp_src= NULL;
+    FILE *fsrc = NULL;
+    FILE *fout = NULL;
+    char *line = NULL;
+    size_t len = 0;
+    ssize_t rd;
+
+    TRACE_FLOW_ENTRY();
+
+    error = expand_path(srcdir, &exp_src);
+    if (error) {
+        INIOUT(printf("Expand path returned error %d\n", error));
+        return error;
+    }
+
+    INIOUT(printf("Source file: %s\n", srcfile));
+    INIOUT(printf("Output file: %s\n", destfile));
+
+    fsrc = fopen(srcfile, "r");
+    if (!fsrc) {
+        error = errno;
+        free(exp_src);
+        INIOUT(printf("Failed to open source file %d\n", error));
+        return error;
+    }
+
+    fout = fopen(destfile, "w");
+    if (!fsrc) {
+        error = errno;
+        fclose(fsrc);
+        free(exp_src);
+        INIOUT(printf("Failed to open output file %d\n", error));
+        return error;
+    }
+
+    INIOUT(printf("Path %s\n", exp_src));
+
+    while ((rd = getline(&line, &len, fsrc)) != -1) {
+        if (strchr(line, '%')) fprintf(fout, line, exp_src, "/ini/ini.d");
+        else fprintf(fout, "%s", line);
+    }
+
+    if (line)
+        free(line);
+
+    fclose(fsrc);
+    fclose(fout);
+    free(exp_src);
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+/* Function to print contents of the list */
+void print_list(struct ref_array *list)
+{
+    uint32_t i = 0;
+    char *ret = NULL;
+    void *ptr = NULL;
+
+    for (;;) {
+        ptr = ref_array_get(list, i, &ret);
+        if (ptr) {
+            INIOUT(printf("%s\n", ret));
+            i++;
+        }
+        else break;
+    }
+}
+
+/* Function to print contents of the list */
+int print_list_to_file(struct ref_array *list,
+                       const char *filename,
+                       const char *mode)
+{
+    uint32_t i = 0;
+    char *ret = NULL;
+    void *ptr = NULL;
+    FILE *file = NULL;
+
+    file = fopen(filename, mode);
+    if (file) {
+        for (;;) {
+            ptr = ref_array_get(list, i, &ret);
+            if (ptr) {
+                fprintf(file,"%s\n", ret);
+                i++;
+            }
+           else break;
+        }
+    }
+    else {
+        printf("Failed to open file for results\n");
+        return -1;
+    }
+    fclose(file);
+    return 0;
+}
+
+
+/* Basic test */
+static int basic_test(void)
+{
+    int error = EOK;
+    int error1 = EOK;
+    char indir[PATH_MAX];
+    char srcname[PATH_MAX];
+    char filename[PATH_MAX];
+    char resname[PATH_MAX];
+    char command[PATH_MAX * 3];
+    char *builddir = NULL;
+    char *srcdir = NULL;
+    struct ini_cfgobj *in_cfg = NULL;
+    struct ini_cfgobj *result_cfg = NULL;
+    struct ref_array *error_list = NULL;
+    struct ref_array *success_list = NULL;
+    struct access_check ac = { INI_ACCESS_CHECK_MODE,
+                               0,
+                               0,
+                               0444,
+                               0444 };
+
+    /* Match all that do not start with 'r'
+    * and end with '.conf' and then match all
+    * ending with '.conf' */
+    const char **patterns =  (const char *[]) { "#",
+                                                "^[^r][a-z]*\\.conf$",
+                                                "^real\\.conf$",
+                                                NULL };
+
+    /* Match all that do not start with 'r'
+    * and end with '.conf' and then match all
+    * ending with '.conf' */
+    const char **sections =  (const char *[]) { "config",
+                                                "monitor",
+                                                "domains",
+                                                "services",
+                                                "provider",
+                                                NULL };
+
+
+    INIOUT(printf("<==== Start ====>\n"));
+
+    srcdir = getenv("srcdir");
+
+    builddir = getenv("builddir");
+
+    snprintf(indir, PATH_MAX, "%s/ini/ini.d",
+                    (srcdir == NULL) ? "." : srcdir);
+
+
+    /* When run in dev environment there can be some temp files which
+     * we need to clean. */
+    snprintf(command, PATH_MAX * 3, "rm %s/*~", indir);
+    system(command);
+
+    /* Make the file path independent */
+    snprintf(srcname, PATH_MAX, "%s/ini/ini.d/merge.validator",
+                    (srcdir == NULL) ? "." : srcdir);
+
+    snprintf(filename, PATH_MAX, "%s/merge.validator.in",
+                      (builddir == NULL) ? "." : builddir);
+
+
+    snprintf(resname, PATH_MAX, "%s/merge.validator.out",
+                      (builddir == NULL) ? "." : builddir);
+
+    /* Prepare results file so that we can compare results */
+    error = prepare_results(srcdir, srcname, filename);
+    if (error) {
+        INIOUT(printf("Failed to results file. Error %d.\n", error));
+        return error;
+    }
+
+    /* Create config collection */
+    error = ini_config_create(&in_cfg);
+    if (error) {
+        INIOUT(printf("Failed to create collection. Error %d.\n", error));
+        return error;
+    }
+
+    error = ini_config_augment(in_cfg,
+                               indir,
+                               patterns,
+                               sections,
+                               &ac,
+                               INI_STOP_ON_NONE,
+                               INI_MV1S_DETECT|INI_MV2S_DETECT|INI_MS_DETECT,
+                               INI_PARSE_NOSPACE|INI_PARSE_NOTAB,
+                               INI_MV2S_DETECT|INI_MS_DETECT,
+                               &result_cfg,
+                               &error_list,
+                               &success_list);
+    if (error) {
+        INIOUT(printf("Augmentation failed with error %d!\n", error));
+    }
+
+    print_list(error_list);
+    print_list(success_list);
+
+    if (!result_cfg) {
+        error = -1;
+        printf("Configuration is empty.\n");
+    }
+    else INIOUT(col_debug_collection(result_cfg->cfg, COL_TRAVERSE_DEFAULT));
+
+    error = print_list_to_file(error_list, "merge.validator.out", "w");
+    error1 = print_list_to_file(success_list, "merge.validator.out", "a");
+    /* Save error */
+    if ((error1 !=0) && (error == 0)) error = error1;
+
+    snprintf(command, PATH_MAX * 3, "diff -q %s %s", filename, resname);
+    error = system(command);
+    INIOUT(printf("Comparison of %s %s returned: %d\n",
+                  filename, resname, error));
+
+    if ((error) || (WEXITSTATUS(error))) {
+        printf("Failed to run diff command %d %d.\n",  error,
+               WEXITSTATUS(error));
+        ref_array_destroy(error_list);
+        ref_array_destroy(success_list);
+        ini_config_destroy(in_cfg);
+        ini_config_destroy(result_cfg);
+        return -1;
+    }
+
+    /* Cleanup */
+    ref_array_destroy(error_list);
+    ref_array_destroy(success_list);
+    ini_config_destroy(in_cfg);
+    ini_config_destroy(result_cfg);
+
+    INIOUT(printf("<==== End ====>\n"));
+
+    return error;
+}
+
+
+int main(int argc, char *argv[])
+{
+    int error = EOK;
+    test_fn tests[] = { basic_test,
+                        NULL };
+    test_fn t;
+    int i = 0;
+    char *var;
+
+    if ((argc > 1) && (strcmp(argv[1], "-v") == 0)) verbose = 1;
+    else {
+        var = getenv("COMMON_TEST_VERBOSE");
+        if (var) verbose = 1;
+    }
+
+    INIOUT(printf("Start\n"));
+
+    while ((t = tests[i++])) {
+        error = t();
+        if (error) {
+            printf("Failed with error %d!\n", error);
+            return error;
+        }
+    }
+
+    INIOUT(printf("Success!\n"));
+    return 0;
+}
-- 
1.7.1

>From 8f0209cb995fd88e71e96efb0b98a0256df58d5d Mon Sep 17 00:00:00 2001
From: Dmitri Pal <d...@dpal.csb>
Date: Sun, 7 Sep 2014 16:43:31 +0200
Subject: [PATCH 4/6] [INI] Refactored access control check

The patch includes implementation of the new internal function.
---
 ini/ini_fileobj.c |   66 +++++++++++++++++++++++++++++++++++++---------------
 1 files changed, 47 insertions(+), 19 deletions(-)

diff --git a/ini/ini_fileobj.c b/ini/ini_fileobj.c
index da8339e..252c23e 100644
--- a/ini/ini_fileobj.c
+++ b/ini/ini_fileobj.c
@@ -705,14 +705,13 @@ const struct stat *ini_config_get_stat(struct ini_cfgfile *file_ctx)
     return ret;
 }
 
-
 /* Check access */
-int ini_config_access_check(struct ini_cfgfile *file_ctx,
-                            uint32_t flags,
-                            uid_t uid,
-                            gid_t gid,
-                            mode_t mode,
-                            mode_t mask)
+int access_check_int(struct stat *file_stats,
+                     uint32_t flags,
+                     uid_t uid,
+                     gid_t gid,
+                     mode_t mode,
+                     mode_t mask)
 {
     mode_t st_mode;
 
@@ -722,23 +721,18 @@ int ini_config_access_check(struct ini_cfgfile *file_ctx,
              INI_ACCESS_CHECK_GID |
              INI_ACCESS_CHECK_UID;
 
-    if ((file_ctx == NULL) || (flags == 0)) {
+    if (flags == 0) {
         TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL);
         return EINVAL;
     }
 
-    if (file_ctx->stats_read == 0) {
-        TRACE_ERROR_NUMBER("Stats were not collected.", EINVAL);
-        return EINVAL;
-    }
-
     /* Check mode */
     if (flags & INI_ACCESS_CHECK_MODE) {
 
         TRACE_INFO_NUMBER("File mode as saved.",
-                          file_ctx->file_stats.st_mode);
+                          file_stats->st_mode);
 
-        st_mode = file_ctx->file_stats.st_mode;
+        st_mode = file_stats->st_mode;
         st_mode &= S_IRWXU | S_IRWXG | S_IRWXO;
         TRACE_INFO_NUMBER("File mode adjusted.", st_mode);
 
@@ -761,8 +755,8 @@ int ini_config_access_check(struct ini_cfgfile *file_ctx,
 
     /* Check uid */
     if (flags & INI_ACCESS_CHECK_UID) {
-        if (file_ctx->file_stats.st_uid != uid) {
-            TRACE_ERROR_NUMBER("GID:", file_ctx->file_stats.st_uid);
+        if (file_stats->st_uid != uid) {
+            TRACE_ERROR_NUMBER("GID:", file_stats->st_uid);
             TRACE_ERROR_NUMBER("GID passed in.", uid);
             TRACE_ERROR_NUMBER("Access denied.", EACCES);
             return EACCES;
@@ -771,8 +765,8 @@ int ini_config_access_check(struct ini_cfgfile *file_ctx,
 
     /* Check gid */
     if (flags & INI_ACCESS_CHECK_GID) {
-        if (file_ctx->file_stats.st_gid != gid) {
-            TRACE_ERROR_NUMBER("GID:", file_ctx->file_stats.st_gid);
+        if (file_stats->st_gid != gid) {
+            TRACE_ERROR_NUMBER("GID:", file_stats->st_gid);
             TRACE_ERROR_NUMBER("GID passed in.", gid);
             TRACE_ERROR_NUMBER("Access denied.", EACCES);
             return EACCES;
@@ -784,6 +778,40 @@ int ini_config_access_check(struct ini_cfgfile *file_ctx,
 
 }
 
+/* Check access */
+int ini_config_access_check(struct ini_cfgfile *file_ctx,
+                            uint32_t flags,
+                            uid_t uid,
+                            gid_t gid,
+                            mode_t mode,
+                            mode_t mask)
+{
+    int error = EOK;
+
+    TRACE_FLOW_ENTRY();
+
+    if (file_ctx == NULL) {
+        TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL);
+        return EINVAL;
+    }
+
+    if (file_ctx->stats_read == 0) {
+        TRACE_ERROR_NUMBER("Stats were not collected.", EINVAL);
+        return EINVAL;
+    }
+
+    error =  access_check_int(&(file_ctx->file_stats),
+                              flags,
+                              uid,
+                              gid,
+                              mode,
+                              mask);
+
+    TRACE_FLOW_EXIT();
+    return error;
+
+}
+
 /* Determines if two file contexts are different by comparing:
  * - time stamp
  * - device ID
-- 
1.7.1

>From d91b8bd98854a00448c021a42a551e5700a6ab82 Mon Sep 17 00:00:00 2001
From: Dmitri Pal <d...@dpal.csb>
Date: Sun, 7 Sep 2014 22:53:19 +0200
Subject: [PATCH 5/6] [INI] Test file for unit test

---
 ini/ini.d/merge.validator |   60 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 60 insertions(+), 0 deletions(-)
 create mode 100644 ini/ini.d/merge.validator

diff --git a/ini/ini.d/merge.validator b/ini/ini.d/merge.validator
new file mode 100644
index 0000000..dc6d6aa
--- /dev/null
+++ b/ini/ini.d/merge.validator
@@ -0,0 +1,60 @@
+File merge.validator did not match provided patterns. Skipping.
+File real8.conf did not match provided patterns. Skipping.
+File new_line.conf did not match provided patterns. Skipping.
+File real32be.conf did not match provided patterns. Skipping.
+File real32le.conf did not match provided patterns. Skipping.
+File real16be.conf did not match provided patterns. Skipping.
+File real16le.conf did not match provided patterns. Skipping.
+File foo.conf.in did not match provided patterns. Skipping.
+Errors detected while parsing: %s%s/comment.conf.
+Error (9) on line 22: Invalid space character at the beginning of the line.
+Error (9) on line 24: Invalid space character at the beginning of the line.
+Error (9) on line 26: Invalid space character at the beginning of the line.
+Error (15) on line 32: Incomplete comment at the end of the file.
+No sections found in file %s%s/comment.conf. Skipping.
+Section [section_a] found in file %s%s/first.conf is not allowed.
+Section [section_c] found in file %s%s/first.conf is not allowed.
+Section [section_b] found in file %s%s/first.conf is not allowed.
+File %s%s/first.conf contains sections that are not allowed. Skipping.
+Section [section_a] found in file %s%s/mergecheck.conf is not allowed.
+Section [section_c] found in file %s%s/mergecheck.conf is not allowed.
+Section [section_b] found in file %s%s/mergecheck.conf is not allowed.
+Section [section_d] found in file %s%s/mergecheck.conf is not allowed.
+File %s%s/mergecheck.conf contains sections that are not allowed. Skipping.
+Section [service] found in file %s%s/mysssd.conf is not allowed.
+Section [sssd] found in file %s%s/mysssd.conf is not allowed.
+Section [nss] found in file %s%s/mysssd.conf is not allowed.
+Section [pam] found in file %s%s/mysssd.conf is not allowed.
+Section [domain] found in file %s%s/mysssd.conf is not allowed.
+File %s%s/mysssd.conf contains sections that are not allowed. Skipping.
+Section [section_a] found in file %s%s/second.conf is not allowed.
+Section [section_b] found in file %s%s/second.conf is not allowed.
+Section [section_d] found in file %s%s/second.conf is not allowed.
+File %s%s/second.conf contains sections that are not allowed. Skipping.
+Section [section1] found in file %s%s/sexpect.conf is not allowed.
+Section [section2] found in file %s%s/sexpect.conf is not allowed.
+File %s%s/sexpect.conf contains sections that are not allowed. Skipping.
+Section [section1] found in file %s%s/smerge.conf is not allowed.
+Section [section2] found in file %s%s/smerge.conf is not allowed.
+File %s%s/smerge.conf contains sections that are not allowed. Skipping.
+Errors detected while parsing: %s%s/space.conf.
+Error (9) on line 1: Invalid space character at the beginning of the line.
+Error (9) on line 2: Invalid space character at the beginning of the line.
+Error (9) on line 3: Invalid space character at the beginning of the line.
+Error (9) on line 4: Invalid space character at the beginning of the line.
+No sections found in file %s%s/space.conf. Skipping.
+Section [info] found in file %s%s/symbols.conf is not allowed.
+Section [languages] found in file %s%s/symbols.conf is not allowed.
+Section [text] found in file %s%s/symbols.conf is not allowed.
+File %s%s/symbols.conf contains sections that are not allowed. Skipping.
+Errors detected while parsing: %s%s/test.conf.
+Error (9) on line 11: Invalid space character at the beginning of the line.
+Error (9) on line 12: Invalid space character at the beginning of the line.
+Error (9) on line 14: Invalid space character at the beginning of the line.
+Error (9) on line 15: Invalid space character at the beginning of the line.
+Error (9) on line 16: Invalid space character at the beginning of the line.
+Error (9) on line 26: Invalid space character at the beginning of the line.
+Error (9) on line 35: Invalid space character at the beginning of the line.
+No sections found in file %s%s/test.conf. Skipping.
+%s%s/ipa.conf
+%s%s/real.conf
-- 
1.7.1

>From aff0f323b2996df7a8f1539f46ca651d2d9fd5e8 Mon Sep 17 00:00:00 2001
From: Dmitri Pal <d...@dpal.csb>
Date: Sun, 7 Sep 2014 16:38:19 +0200
Subject: [PATCH 1/6] [RA] Print info when array is empty

---
 refarray/ref_array.c |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/refarray/ref_array.c b/refarray/ref_array.c
index 273ac4f..e5becf9 100644
--- a/refarray/ref_array.c
+++ b/refarray/ref_array.c
@@ -548,6 +548,11 @@ void ref_array_debug(struct ref_array *ra, int num)
 {
     int i,j;
 
+    if (!ra) {
+        printf("\nARRAY is NULL\n");
+        return;
+    }
+
     printf("\nARRAY DUMP START\n");
     printf("Length = %u\n", ra->len);
     printf("Size = %u\n", ra->size);
-- 
1.7.1

_______________________________________________
sssd-devel mailing list
sssd-devel@lists.fedorahosted.org
https://lists.fedorahosted.org/mailman/listinfo/sssd-devel

Reply via email to