All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC] Add list and iter data types to libsepol
@ 2007-01-16 21:10 Karl MacMillan
  2007-01-16 22:24 ` Karl MacMillan
  2007-01-17 12:15 ` Stephen Smalley
  0 siblings, 2 replies; 5+ messages in thread
From: Karl MacMillan @ 2007-01-16 21:10 UTC (permalink / raw)
  To: SELinux Mail List

[-- Attachment #1: Type: text/plain, Size: 574 bytes --]

As part of some work to improve the parser in checkpolicy and module 
data structures in libsepol, I have implemented doubly-linked list and 
iterator data types. The attached patch adds these to libsepol.

I would appreciate feedback on these before I submit larger patches that 
make use of them. In particular:

* Is the iterator concept acceptable (I plan to add iterator support to 
other data types including hashtabs)?

* We've discussed dropping the use of typedefs on structs (which I do in 
this patch). Is this what we want to do, or should I add typedefs?

Karl

[-- Attachment #2: sepol-list-iter.patch --]
[-- Type: text/x-patch, Size: 20785 bytes --]

diff -r 20ff5c9a577b libsepol/include/sepol/errcodes.h
--- a/libsepol/include/sepol/errcodes.h	Tue Jan 16 15:50:54 2007 -0500
+++ b/libsepol/include/sepol/errcodes.h	Tue Jan 16 16:04:16 2007 -0500
@@ -22,4 +22,7 @@
 #define SEPOL_EEXIST         -EEXIST
 #define SEPOL_ENOENT         -ENOENT
 
+/* Custom error codes */
+#define SEPOL_ITERSTOP       -500
+
 #endif
diff -r 20ff5c9a577b libsepol/include/sepol/policydb/iter.h
--- a/libsepol/include/sepol/policydb/iter.h	Tue Jan 16 15:50:54 2007 -0500
+++ b/libsepol/include/sepol/policydb/iter.h	Tue Jan 16 16:04:16 2007 -0500
@@ -0,0 +1,118 @@
+/* Author : Karl MacMillan <kmacmillan@mentalrootkit.com> */
+
+#ifndef __sepol_iter_h__
+#define __sepol_iter_h__
+
+/* Iterators represent a position within a data-structure. They are a
+ * generalization of the concept of pointers. Using iterators allows
+ * algorithms to be cleanly separated from data structures by
+ * abstracting the concept of position and movement. Once an iterator
+ * is created and its position initialized in a data structure
+ * specific way, looping and other control flow can be implemented
+ * only using the generic iterator functions.
+ *
+ * For example, consider the code below which loops through a list
+ * (the error handling is ommitted for brevity):
+ *
+ *   int ret;
+ *   struct sepol_iter *iter;
+ *
+ *   sepol_iter_create(&iter);
+ *   ret = sepol_list_begin(iter);
+ *
+ *   while (ret == SEPOL_OK) {
+ *      // process the data
+ *      data = sepol_iter_get_data(iter);
+ *      ret = sepol_iter_next(iter);
+ *   }
+ *   if (ret != SEPOL_ITERSTOP)
+ *      // handle errors
+ */
+struct sepol_iter;
+
+/* Create an iterator. The iterator is not valid
+ * until a data structure specific call has been
+ * made to initiliaze its position (e.g., sepol_list_begin).
+ *
+ * Returns:
+ *   SEPOL_OK: success
+ *   SEPOL_ENOMEM: out of memory
+ */
+int sepol_iter_create(struct sepol_iter **iter);
+
+/* Destroy an iterator.
+ */
+void sepol_iter_destroy(struct sepol_iter *iter);
+
+/* Return the data at this iterator location. The type
+ * of the returned data is data structure specific.
+ *
+ * Returns:
+ *   Non-null: data at this iterator position
+ *   NULL: error
+ */
+void *sepol_iter_get_data(struct sepol_iter *iter);
+
+/* Move the iterator to the next position. If SEPOL_ITERSTOP is
+ * returned, one past the end of the data structure has been reached.
+ * No other calls using this iterator are valid until a data structure
+ * specific call has been made to reset it to a valid position (e.g.,
+ * sepol_list_begin).
+ *
+ * Returns:
+ *   SEPOL_OK: iterator successfully moved
+ *   SEPOL_ITERSTOP: iteration should stop
+ *   < 0: other errors specific to the underlying data structure
+ */
+int sepol_iter_next(struct sepol_iter *iter);
+
+/* Move the iterator to the prev position. If SEPOL_ITERSTOP is
+ * returned, one before the beginning of the data structure has been
+ * reached. No other calls using this iterator are valid until a data
+ * structure specific call has been made to reset it to a valid
+ * position (e.g., sepol_list_end).
+ *
+ * Not all iterators support prev - the data structure specific
+ * iterator documentation should indicate whether prev is supported.
+ *
+ * Returns:
+ *   SEPOL_OK: iterator successfully moved
+ *   SEPOL_ITERSTOP: iteration should stop
+ *   SEPOL_ENOTSUP: previous iterator not supported for this
+ *     data structure.
+ *   < 0: other errors specific to the underlying data structure
+ */
+int sepol_iter_prev(struct sepol_iter *iter);
+
+/* Move the iterator forward by distance. This does _not_ set the
+ * absolute position of the iterator. Rather, it moves in by distance
+ * from the current position. For example, moving an iterator at
+ * position 2 by 3 would put the iterator at position 5.
+ *
+ * Returns: same as sepol_iter_next
+ */
+int sepol_iter_forward(struct sepol_iter *iter, unsigned int distance);
+
+/* Move the iterator backward by distance. This does _not_ set the
+ * absolute position of the iterator. Rather, it moves in by distance
+ * from the current position. For example, moving an iterator at
+ * position 2 by 1 would put the iterator at position 1.
+ *
+ * Iterator must support sepol_iter_prev.
+ *
+ * Returns: same as sepol_iter_prev
+ */
+int sepol_iter_backward(struct sepol_iter *iter, unsigned int distance);
+
+/* used by implementations of iterators */
+void sepol_iter_set_state(struct sepol_iter *iter, void *state);
+void *sepol_iter_get_state(struct sepol_iter *iter);
+void sepol_iter_set_next(struct sepol_iter *iter,
+			 int (*next)(struct sepol_iter *));
+void sepol_iter_set_prev(struct sepol_iter *iter,
+			 int (*prev)(struct sepol_iter *));
+void sepol_iter_set_get_data(struct sepol_iter *iter,
+			     void *(*get)(struct sepol_iter *));
+void sepol_iter_set_free(struct sepol_iter *iter, void (*state_free)(void *data));
+
+#endif
diff -r 20ff5c9a577b libsepol/include/sepol/policydb/list.h
--- a/libsepol/include/sepol/policydb/list.h	Tue Jan 16 15:50:54 2007 -0500
+++ b/libsepol/include/sepol/policydb/list.h	Tue Jan 16 16:04:16 2007 -0500
@@ -0,0 +1,79 @@
+/* Author : Karl MacMillan <kmacmillan@mentalrootkit.com> */
+
+
+#ifndef __sepol_list_h__
+#define __sepol_list_h__
+
+#include <sepol/policydb/iter.h>
+
+/* Doubly-linked list data type. */
+
+struct sepol_list;
+
+/* Create an sepol_list. On success, SEPOL_OK is returned
+ * and the list pointer passed in is set to an allocated
+ * sepol_list struct that has been initialized.
+ *
+ * Returns:
+ *   SEPOL_OK: sepol_list successfully created
+ *   SEPOL_ENOMEM: out of memory
+ */
+int sepol_list_create(struct sepol_list **list);
+
+/* Destroy an sepol_list. This will _not_ free the memory
+ * for the list items, only for the internal list structures.
+ * The list item memory should be freed by the caller. Note
+ * that this function can return an error.
+ */
+void sepol_list_destroy(struct sepol_list *list);
+
+/* Append an item to the list. If successful, the item will
+ * become the last item in the list.
+ *
+ * Returns:
+ *   SEPOL_OK: success
+ *   SEPOL_ENOMEM: out of memory
+ */
+int sepol_list_append(struct sepol_list *list, void *item);
+
+/* Prepend an item to the list. If successful, the item will
+ * become the first item in the list.
+ *
+ * Returns:
+ *   SEPOL_OK: success
+ *   SEPOL_ENOMEM: out of memory
+ */
+int sepol_list_prepend(struct sepol_list *list, void *item);
+
+/* Insert an item at the iterator position. The existing item
+ * is shifted to the next position in the list. The iterator
+ * continues to point at the same item, though the position of
+ * that item will have moved forward by one.
+ *
+ * Returns:
+ *   SEPOL_OK: success
+ *   SEPOL_ENOMEM: out of memory
+ */
+int sepol_list_insert(struct sepol_list *list, struct sepol_iter *iter, void *item);
+
+/* Position the iterator at the beginning of the list. If the list
+ * is empty SEPOL_ITERSTOP will be returned and the iterator will not
+ * be valid.
+ *
+ * Returns:
+ *   SEPOL_OK: success
+ *   SEPOL_ITERSTOP: empty list
+ */
+int sepol_list_begin(struct sepol_list *list, struct sepol_iter *iter);
+
+/* Position the iterator at the end of the list. If the list
+ * is empty SEPOL_ITERSTOP will be returned and the iterator will not
+ * be valid.
+ *
+ * Returns:
+ *   SEPOL_OK: success
+ *   SEPOL_ITERSTOP: empty list
+ */
+int sepol_list_end(struct sepol_list *list, struct sepol_iter *iter);
+
+#endif
diff -r 20ff5c9a577b libsepol/src/iter.c
--- a/libsepol/src/iter.c	Tue Jan 16 15:50:54 2007 -0500
+++ b/libsepol/src/iter.c	Tue Jan 16 16:04:16 2007 -0500
@@ -0,0 +1,141 @@
+/*
+ * Author : Karl MacMillan <kmacmillan@mentalrootkit.com>
+ *
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This 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 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <sepol/policydb/iter.h>
+
+#include <sepol/errcodes.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+struct sepol_iter
+{
+	void *state;
+	int (*next)(struct sepol_iter *);
+	int (*prev)(struct sepol_iter *);
+	void *(*get)(struct sepol_iter *);
+	void (*free)(void *);
+};
+
+int sepol_iter_create(struct sepol_iter **iter)
+{
+	*iter = (struct sepol_iter*)calloc(1, sizeof(struct sepol_iter));
+	if (*iter == NULL)
+		return SEPOL_ENOMEM;
+
+	return SEPOL_OK;
+}
+
+void sepol_iter_destroy(struct sepol_iter *iter)
+{
+	if (iter->free != NULL)
+		iter->free(iter);
+	free(iter);
+}
+
+void *sepol_iter_get_data(struct sepol_iter *iter)
+{
+	assert(iter);
+	assert(iter->get);
+	return iter->get(iter);
+}
+
+int sepol_iter_next(struct sepol_iter *iter)
+{
+	assert(iter);
+	assert(iter->next);
+	return iter->next(iter);
+}
+
+int sepol_iter_prev(struct sepol_iter *iter)
+{
+	assert(iter);
+
+	if (iter->prev == NULL)
+		return SEPOL_ENOTSUP;
+
+	return iter->prev(iter);
+}
+
+int sepol_iter_forward(struct sepol_iter *iter, unsigned int distance)
+{
+	unsigned int i = 0;
+	int ret = SEPOL_OK;
+	
+	while (i < distance && ret == SEPOL_OK) {
+		ret = sepol_iter_next(iter);
+		i++;
+	}
+
+	return ret;
+}
+
+int sepol_iter_backward(struct sepol_iter *iter, unsigned int distance)
+{
+	unsigned int i = 0;
+	int ret = SEPOL_OK;
+	
+	while (i < distance && ret == SEPOL_OK) {
+		ret = sepol_iter_prev(iter);
+		i++;
+	}
+
+	return ret;
+}
+
+void sepol_iter_set_state(struct sepol_iter *iter, void *state)
+{
+	assert(iter);
+	iter->state = state;
+}
+
+void *sepol_iter_get_state(struct sepol_iter *iter)
+{
+	assert(iter);
+	return iter->state;
+}
+
+void sepol_iter_set_next(struct sepol_iter *iter,
+			 int (*next)(struct sepol_iter *))
+{
+	assert(iter);
+	iter->next = next;
+}
+
+void sepol_iter_set_prev(struct sepol_iter *iter,
+			 int (*prev)(struct sepol_iter *))
+{
+	assert(iter);
+	iter->prev = prev;
+}
+
+void sepol_iter_set_get_data(struct sepol_iter *iter,
+			     void *(*get)(struct sepol_iter *))
+{
+	assert(iter);
+	iter->get = get;
+}
+
+void sepol_iter_set_free(struct sepol_iter *iter, void (*state_free)(void *))
+{
+	assert(iter);
+	iter->free = free;
+}
diff -r 20ff5c9a577b libsepol/src/list.c
--- a/libsepol/src/list.c	Tue Jan 16 15:50:54 2007 -0500
+++ b/libsepol/src/list.c	Tue Jan 16 16:04:16 2007 -0500
@@ -0,0 +1,253 @@
+/*
+ * Author : Karl MacMillan <kmacmillan@mentalrootkit.com>
+ *
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This 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 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <sepol/errcodes.h>
+
+#include <sepol/policydb/list.h>
+#include <sepol/policydb/iter.h>
+
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct sepol_list_item
+{
+	struct sepol_list_item *next;
+	struct sepol_list_item *prev;
+	void *item;
+};
+
+struct sepol_list
+{
+	struct sepol_list_item *head;
+	struct sepol_list_item *tail;
+};
+
+int sepol_list_create(struct sepol_list **list)
+{
+	assert(list);
+
+	*list = (struct sepol_list *)calloc(1, sizeof(struct sepol_list));
+	if (*list == NULL)
+		return SEPOL_ENOMEM;
+
+	return SEPOL_OK;
+}
+
+void sepol_list_destroy(struct sepol_list *list)
+{
+	struct sepol_list_item *cur, *next;
+
+	if (list == NULL)
+		return;
+
+	cur = list->head;
+	while (cur != NULL) {
+		next = cur->next;
+		free(cur);
+		cur = next;
+	}
+
+	free(list);
+}
+
+static int sepol_list_item_create(struct sepol_list_item **x)
+{
+	*x = calloc(1, sizeof(struct sepol_list_item));
+	if (*x == NULL)
+		return SEPOL_ENOMEM;
+	else
+		return SEPOL_OK;
+}
+
+int sepol_list_append(struct sepol_list *list, void *item)
+{
+	int ret;
+	struct sepol_list_item *x;
+
+	assert(list);
+
+	ret = sepol_list_item_create(&x);
+	if (ret < 0)
+		return ret;
+	x->item = item;
+
+	/* empty list */
+	if (list->tail == NULL) {
+		list->tail = x;
+		list->head = x;
+		x->prev = NULL;
+		x->next = NULL;
+	} else {
+		list->tail->next = x;
+		x->prev = list->tail;
+		x->next = NULL;
+		list->tail = x;
+	}
+
+	return SEPOL_OK;
+}
+
+int sepol_list_prepend(struct sepol_list *list, void *item)
+{
+	int ret;
+	struct sepol_list_item *x;
+
+	assert(list);
+
+	ret = sepol_list_item_create(&x);
+	if (ret < 0)
+		return ret;
+	x->item = item;
+
+	/* empty list */
+	if (list->tail == NULL) {
+		list->tail = x;
+		list->head = x;
+		x->prev = NULL;
+		x->next = NULL;
+	} else {
+		x->next = list->head;
+		list->head->prev = x;
+		x->prev = NULL;
+		list->head = x;
+	}
+
+	return SEPOL_OK;
+}
+
+int sepol_list_insert(struct sepol_list *list, struct sepol_iter *iter, void *item)
+{
+	int ret;
+	struct sepol_list_item *x, *cur;
+
+	assert(list);
+
+	cur = (struct sepol_list_item*)sepol_iter_get_state(iter);
+	assert(cur);
+
+	/* Short circuit for prepend and append. */
+	if (list->head == cur) {
+		return sepol_list_prepend(list, item);
+	} else if (list->tail == cur) {
+		return sepol_list_append(list, item);
+	}
+
+	ret = sepol_list_item_create(&x);
+	if (ret < 0)
+		return ret;
+	x->item = item;
+
+	x->prev = cur->prev;
+	x->prev->next = x;
+	x->next = cur;
+	cur->prev = x;
+	
+	return SEPOL_OK;
+}
+
+static int sepol_list_next(struct sepol_iter *iter)
+{
+	struct sepol_list_item *cur;
+
+	assert(iter);
+
+	cur = (struct sepol_list_item*)sepol_iter_get_state(iter);
+	assert(cur);
+
+	if (cur->next == NULL) {
+		/* set the state to NULL to catch using this iterator
+		 * after ITERSTOP is returned (which is a bug). */
+		sepol_iter_set_state(iter, NULL);
+		return SEPOL_ITERSTOP;
+	} else {
+		sepol_iter_set_state(iter, cur->next);
+		return SEPOL_OK;
+	}
+}
+
+static int sepol_list_prev(struct sepol_iter *iter)
+{
+	struct sepol_list_item *cur;
+
+	assert(iter);
+
+	cur = (struct sepol_list_item*)sepol_iter_get_state(iter);
+	assert(cur);
+
+	if (cur->prev == NULL) {
+		/* set the state to NULL to catch using this iterator
+		 * after ITERSTOP is returned (which is a bug). */
+		sepol_iter_set_state(iter, NULL);
+		return SEPOL_ITERSTOP;
+	} else {
+		sepol_iter_set_state(iter, cur->prev);
+		return SEPOL_OK;
+	}	
+}
+
+static void *sepol_list_iter_get_data(struct sepol_iter *iter)
+{
+	struct sepol_list_item *cur;
+
+	assert(iter);
+
+	cur = (struct sepol_list_item*)sepol_iter_get_state(iter);
+	assert(cur);
+
+	return cur->item;
+}
+
+#define ITERSETUP(iter) { sepol_iter_set_next(iter, sepol_list_next); \
+                          sepol_iter_set_prev(iter, sepol_list_prev); \
+                          sepol_iter_set_get_data(iter, sepol_list_iter_get_data); }
+
+
+int sepol_list_begin(struct sepol_list *list, struct sepol_iter *iter)
+{
+	assert(list);
+	assert(iter);
+
+	if (list->head == NULL)
+		return SEPOL_ITERSTOP;
+
+	sepol_iter_set_state(iter, list->head);
+
+	ITERSETUP(iter);
+
+	return SEPOL_OK;
+}
+
+int sepol_list_end(struct sepol_list *list, struct sepol_iter *iter)
+{
+	assert(list);
+	assert(iter);
+
+	if (list->tail == NULL)
+		return SEPOL_ITERSTOP;
+
+	sepol_iter_set_state(iter, list->tail);
+
+	ITERSETUP(iter);
+
+	return SEPOL_OK;
+}
+
diff -r 20ff5c9a577b libsepol/tests/libsepol-tests.c
--- a/libsepol/tests/libsepol-tests.c	Tue Jan 16 15:50:54 2007 -0500
+++ b/libsepol/tests/libsepol-tests.c	Tue Jan 16 16:04:16 2007 -0500
@@ -22,6 +22,7 @@
 #include "test-linker.h"
 #include "test-expander.h"
 #include "test-deps.h"
+#include "test-list.h"
 
 #include <CUnit/Basic.h>
 #include <CUnit/Console.h>
@@ -61,6 +62,7 @@ static int do_tests(int interactive, int
 	DECLARE_SUITE(linker);
 	DECLARE_SUITE(expander);
 	DECLARE_SUITE(deps);
+	DECLARE_SUITE(list);
 
 	if (verbose)
 		CU_basic_set_mode(CU_BRM_VERBOSE);
diff -r 20ff5c9a577b libsepol/tests/test-list.c
--- a/libsepol/tests/test-list.c	Tue Jan 16 15:50:54 2007 -0500
+++ b/libsepol/tests/test-list.c	Tue Jan 16 16:04:16 2007 -0500
@@ -0,0 +1,145 @@
+/*
+ * Author : Karl MacMillan <kmacmillan@mentalrootkit.com>
+ *
+ * Copyright (C) 2006 Red Hat, Inc.
+ *
+ * This 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 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "test-list.h"
+
+#include <sepol/policydb/list.h>
+#include <sepol/policydb/iter.h>
+#include <sepol/errcodes.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int list_test_init(void)
+{
+	return 0;
+}
+
+int list_test_cleanup(void)
+{
+	return 0;
+}
+
+static void test_list_create(void)
+{
+	struct sepol_list *list;
+	int ret;
+	
+	ret = sepol_list_create(&list);
+	CU_ASSERT(ret == 0);
+
+	sepol_list_destroy(list);
+}
+
+static void test_list(void)
+{
+	struct sepol_list *list;
+	struct sepol_iter *iter;
+	int ret;
+	int i, *num, nums[6];
+
+	for (i = 0; i < 6; i++)
+		nums[i] = i;
+	
+	ret = sepol_iter_create(&iter);
+	CU_ASSERT(ret == 0);
+
+	ret = sepol_list_create(&list);
+	CU_ASSERT(ret == 0);
+
+	ret = sepol_list_append(list, &nums[4]);
+	CU_ASSERT(ret == 0);
+
+	ret = sepol_list_append(list, &nums[5]);
+	CU_ASSERT(ret == 0);
+
+	ret = sepol_list_prepend(list, &nums[1]);
+	CU_ASSERT(ret == 0);
+
+	ret = sepol_list_begin(list, iter);
+	CU_ASSERT(ret == 0);
+	ret = sepol_iter_forward(iter, 1);
+	CU_ASSERT(ret == 0);
+
+	ret = sepol_list_insert(list, iter, &nums[2]);
+	CU_ASSERT(ret == 0);
+
+	ret = sepol_list_begin(list, iter);
+	CU_ASSERT(ret == 0);
+	ret = sepol_list_insert(list, iter, &nums[0]);
+	CU_ASSERT(ret == 0);
+
+	ret = sepol_list_begin(list, iter);
+	CU_ASSERT(ret == 0);
+	ret = sepol_iter_forward(iter, 3);
+	CU_ASSERT(ret == 0);
+
+	ret = sepol_list_insert(list, iter, &nums[3]);
+	CU_ASSERT(ret == 0);
+
+	ret = sepol_list_begin(list, iter);
+	CU_ASSERT(ret == 0);
+	i = 0;
+	while (ret == SEPOL_OK) {
+		num = (int*)sepol_iter_get_data(iter);
+		CU_ASSERT(*num == i);
+		i++;
+		ret = sepol_iter_next(iter);
+	}
+	CU_ASSERT(i == 6);
+	CU_ASSERT(ret == SEPOL_ITERSTOP);
+
+	ret = sepol_list_end(list, iter);
+	CU_ASSERT(ret == 0);
+	i = 5;
+	while (ret == SEPOL_OK) {
+		num = (int*)sepol_iter_get_data(iter);
+		CU_ASSERT(*num == i);
+		i--;
+		ret = sepol_iter_prev(iter);		
+	}
+	CU_ASSERT(i == -1);
+	CU_ASSERT(ret == SEPOL_ITERSTOP);
+
+	ret = sepol_list_end(list, iter);
+	CU_ASSERT(ret == 0);
+	ret = sepol_iter_prev(iter);
+	CU_ASSERT(ret == 0);
+	num = (int*)sepol_iter_get_data(iter);
+	CU_ASSERT(*num == 4);
+	
+
+	sepol_iter_destroy(iter);
+	sepol_list_destroy(list);
+}
+
+int list_add_tests(CU_pSuite suite)
+{
+	if (NULL == CU_add_test(suite, "test_list_create", test_list_create)) {
+		return -1;
+	}
+
+	if (NULL == CU_add_test(suite, "test_list",
+				test_list)) {
+		return -1;
+	}
+
+	return 0;
+}
diff -r 20ff5c9a577b libsepol/tests/test-list.h
--- a/libsepol/tests/test-list.h	Tue Jan 16 15:50:54 2007 -0500
+++ b/libsepol/tests/test-list.h	Tue Jan 16 16:04:16 2007 -0500
@@ -0,0 +1,12 @@
+/* Author : Karl MacMillan <kmacmillan@mentalrootkit.com> */
+
+#ifndef __test_list_h__
+#define __test_list_h__
+
+#include <CUnit/Basic.h>
+
+int list_test_init(void);
+int list_test_cleanup(void);
+int list_add_tests(CU_pSuite suite);
+
+#endif

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC] Add list and iter data types to libsepol
  2007-01-16 21:10 [RFC] Add list and iter data types to libsepol Karl MacMillan
@ 2007-01-16 22:24 ` Karl MacMillan
  2007-01-17 12:15 ` Stephen Smalley
  1 sibling, 0 replies; 5+ messages in thread
From: Karl MacMillan @ 2007-01-16 22:24 UTC (permalink / raw)
  To: Karl MacMillan; +Cc: SELinux Mail List

[-- Attachment #1: Type: text/plain, Size: 584 bytes --]

Karl MacMillan wrote:
> As part of some work to improve the parser in checkpolicy and module 
> data structures in libsepol, I have implemented doubly-linked list and 
> iterator data types. The attached patch adds these to libsepol.
> 
> I would appreciate feedback on these before I submit larger patches that 
> make use of them. In particular:
> 
> * Is the iterator concept acceptable (I plan to add iterator support to 
> other data types including hashtabs)?
> 

Attached is a patch that implements iterators for hash tables (it also 
includes some unrelated cleanups).

Karl


[-- Attachment #2: sepol-hashtab-iter.patch --]
[-- Type: text/x-patch, Size: 9171 bytes --]

diff -r fceafe45b35c libsepol/include/sepol/policydb/hashtab.h
--- a/libsepol/include/sepol/policydb/hashtab.h	Tue Jan 16 17:22:30 2007 -0500
+++ b/libsepol/include/sepol/policydb/hashtab.h	Tue Jan 16 17:22:40 2007 -0500
@@ -15,6 +15,7 @@
 #define _SEPOL_POLICYDB_HASHTAB_H_
 
 #include <sepol/errcodes.h>
+#include <sepol/policydb/iter.h>
 
 #include <stdint.h>
 #include <stdio.h>
@@ -40,19 +41,18 @@ typedef struct hashtab_val {
 
 typedef hashtab_val_t *hashtab_t;
 
+typedef unsigned int (*hash_value_fnc_t)(hashtab_t h, const hashtab_key_t key);
+typedef int (*keycmp_fnc_t)(hashtab_t h, const hashtab_key_t key1,
+		      const hashtab_key_t key2);
 /*
    Creates a new hash table with the specified characteristics.
 
    Returns NULL if insufficent space is available or
    the new hash table otherwise.
  */
-extern hashtab_t hashtab_create(unsigned int (*hash_value) (hashtab_t h,
-							    const hashtab_key_t
-							    key),
-				int (*keycmp) (hashtab_t h,
-					       const hashtab_key_t key1,
-					       const hashtab_key_t key2),
+extern hashtab_t hashtab_create(hash_value_fnc_t hash, keycmp_fnc_t keycmp,
 				unsigned int size);
+
 /*
    Inserts the specified (key, datum) pair into the specified hash table.
 
@@ -61,6 +61,9 @@ extern hashtab_t hashtab_create(unsigned
    SEPOL_OK otherwise.
  */
 extern int hashtab_insert(hashtab_t h, hashtab_key_t k, hashtab_datum_t d);
+extern int hashtab_insert2(hashtab_t h, hashtab_key_t key, hashtab_datum_t datum,
+			   hashtab_ptr_t *current);
+
 
 /*
    Removes the entry with the specified key from the hash table.
@@ -103,6 +106,16 @@ extern void hashtab_destroy(hashtab_t h)
 extern void hashtab_destroy(hashtab_t h);
 
 /*
+  Initializes an iterator for the hash table (see
+  sepol/policydb/iter.h).  sepol_iter_get_data will return a
+  hashtab_ptr_t.
+
+  Returns NULL if the iterator cannot be created or the iterator
+  otherwise.
+ */
+extern int hashtab_iter(hashtab_t h, struct sepol_iter *iter);
+
+/*
    Applies the specified apply function to (key,datum,args)
    for each entry in the specified hash table.
 
diff -r fceafe45b35c libsepol/src/hashtab.c
--- a/libsepol/src/hashtab.c	Tue Jan 16 17:22:30 2007 -0500
+++ b/libsepol/src/hashtab.c	Tue Jan 16 17:22:40 2007 -0500
@@ -28,9 +28,11 @@
  * Implementation of the hash table type.
  */
 
+#include <sepol/policydb/hashtab.h>
+
 #include <stdlib.h>
 #include <string.h>
-#include <sepol/policydb/hashtab.h>
+#include <assert.h>
 
 hashtab_t hashtab_create(unsigned int (*hash_value) (hashtab_t h,
 						     const hashtab_key_t key),
@@ -63,7 +65,8 @@ hashtab_t hashtab_create(unsigned int (*
 	return p;
 }
 
-int hashtab_insert(hashtab_t h, hashtab_key_t key, hashtab_datum_t datum)
+int hashtab_insert2(hashtab_t h, hashtab_key_t key, hashtab_datum_t datum,
+		    hashtab_ptr_t *current)
 {
 	int hvalue;
 	hashtab_ptr_t prev, cur, newnode;
@@ -79,8 +82,11 @@ int hashtab_insert(hashtab_t h, hashtab_
 		cur = cur->next;
 	}
 
-	if (cur && (h->keycmp(h, key, cur->key) == 0))
+	if (cur && (h->keycmp(h, key, cur->key) == 0)) {
+		if (current)
+			*current = cur;
 		return SEPOL_EEXIST;
+	}
 
 	newnode = (hashtab_ptr_t) malloc(sizeof(hashtab_node_t));
 	if (newnode == NULL)
@@ -98,6 +104,11 @@ int hashtab_insert(hashtab_t h, hashtab_
 
 	h->nel++;
 	return SEPOL_OK;
+}
+
+int hashtab_insert(hashtab_t h, hashtab_key_t key, hashtab_datum_t datum)
+{
+ 	return hashtab_insert2(h, key, datum, NULL);
 }
 
 int hashtab_remove(hashtab_t h, hashtab_key_t key,
@@ -219,6 +230,84 @@ void hashtab_destroy(hashtab_t h)
 	free(h);
 }
 
+/* to support removal of items, a prev hashtab_ptr_t will
+ * need to be added.
+ */
+struct hashtab_iter_state
+{
+	hashtab_t h;
+	unsigned int bucket;
+	hashtab_ptr_t cur;
+};
+
+static void hashtab_iter_destroy(void *data)
+{
+	free(data);
+}
+
+
+static int hashtab_state_next_internal(struct hashtab_iter_state *state)
+{
+	if (state->cur == NULL) {
+		for (state->bucket++;
+		     state->bucket < state->h->size; state->bucket++) {
+			state->cur = state->h->htable[state->bucket];
+			if (state->cur)
+				break;
+		}
+	}
+	
+	if (state->cur == NULL) {
+		return SEPOL_ITERSTOP;
+	} else {
+		return SEPOL_OK;
+	}
+
+}
+
+static int hashtab_iter_next(struct sepol_iter *iter)
+{
+	struct hashtab_iter_state *state = sepol_iter_get_state(iter);
+
+	if (state->cur != NULL)
+		state->cur = state->cur->next;
+	return hashtab_state_next_internal(state);
+}
+
+static void *hashtab_iter_get_data(struct sepol_iter *iter)
+{
+	struct hashtab_iter_state *state = sepol_iter_get_state(iter);
+	assert(state);
+	return state->cur;
+}
+
+int hashtab_iter(hashtab_t h, struct sepol_iter *iter)
+{
+	struct hashtab_iter_state *state;
+
+	assert(iter);
+
+	state = sepol_iter_get_state(iter);
+	if (state)
+		free(state);
+
+	state = malloc(sizeof(struct hashtab_iter_state));
+	if (state == NULL) {
+		free(state);
+		return ENOMEM;
+	}
+
+	state->h = h;
+	state->bucket = 0;
+	state->cur = h->htable[0];
+	sepol_iter_set_state(iter, state);
+	sepol_iter_set_next(iter, hashtab_iter_next);
+	sepol_iter_set_free(iter, hashtab_iter_destroy);
+	sepol_iter_set_get_data(iter, hashtab_iter_get_data);
+
+	return hashtab_state_next_internal(state);
+}
+
 int hashtab_map(hashtab_t h,
 		int (*apply) (hashtab_key_t k,
 			      hashtab_datum_t d, void *args), void *args)
diff -r fceafe45b35c libsepol/tests/libsepol-tests.c
--- a/libsepol/tests/libsepol-tests.c	Tue Jan 16 17:22:30 2007 -0500
+++ b/libsepol/tests/libsepol-tests.c	Tue Jan 16 17:22:40 2007 -0500
@@ -23,6 +23,7 @@
 #include "test-expander.h"
 #include "test-deps.h"
 #include "test-list.h"
+#include "test-hashtab.h"
 
 #include <CUnit/Basic.h>
 #include <CUnit/Console.h>
@@ -63,6 +64,7 @@ static int do_tests(int interactive, int
 	DECLARE_SUITE(expander);
 	DECLARE_SUITE(deps);
 	DECLARE_SUITE(list);
+	DECLARE_SUITE(hashtab);
 
 	if (verbose)
 		CU_basic_set_mode(CU_BRM_VERBOSE);
diff -r fceafe45b35c libsepol/tests/test-hashtab.c
--- a/libsepol/tests/test-hashtab.c	Tue Jan 16 17:22:30 2007 -0500
+++ b/libsepol/tests/test-hashtab.c	Tue Jan 16 17:22:40 2007 -0500
@@ -0,0 +1,99 @@
+/*
+ * Author: Karl MacMillan <kmacmillan@mentalrootkit.com>
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ *  This 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 2.1 of the License, or (at your option) any later version.
+ *
+ *  This 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 this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "test-hashtab.h"
+
+#include <sepol/policydb/hashtab.h>
+
+#include <string.h>
+
+int hashtab_test_init(void)
+{
+	return 0;
+}
+
+int hashtab_test_cleanup(void)
+{
+	return 0;
+}
+
+static unsigned int reqsymhash(hashtab_t h, hashtab_key_t key)
+{
+	char *p, *keyp;
+	size_t size;
+	unsigned int val;
+
+	val = 0;
+	keyp = (char *)key;
+	size = strlen(keyp);
+	for (p = keyp; ((size_t) (p - keyp)) < size; p++)
+		val =
+		    (val << 4 | (val >> (8 * sizeof(unsigned int) - 4))) ^ (*p);
+	return val & (h->size - 1);
+}
+
+static int reqsymcmp(hashtab_t h
+		     __attribute__ ((unused)), hashtab_key_t key1,
+		     hashtab_key_t key2)
+{
+	char *keyp1, *keyp2;
+
+	keyp1 = (char *)key1;
+	keyp2 = (char *)key2;
+	return strcmp(keyp1, keyp2);
+}
+
+static void test_hashtab_iter(void)
+{
+	hashtab_t h;
+	hashtab_ptr_t cur;
+	struct sepol_iter *iter;
+	int ret, i;
+	char *strings[] = {"foo", "bar", "baz"};
+
+	h = hashtab_create(reqsymhash, reqsymcmp, 64);
+	CU_ASSERT(h != NULL);
+	
+	ret = hashtab_insert(h, strings[0], (void *)0);
+	ret = hashtab_insert(h, strings[1], (void *)1);
+	ret = hashtab_insert(h, strings[2], (void *)2);
+
+	ret = sepol_iter_create(&iter);
+	CU_ASSERT(ret == SEPOL_OK);
+	ret = hashtab_iter(h, iter);
+	CU_ASSERT(ret == SEPOL_OK);
+	while (ret == SEPOL_OK) {
+		cur = sepol_iter_get_data(iter);
+		i = (int)cur->datum;
+		CU_ASSERT(i < 3);
+		CU_ASSERT(strcmp(strings[i], (char *)cur->key) == 0);
+		ret = sepol_iter_next(iter);
+	}
+	CU_ASSERT(ret == SEPOL_ITERSTOP);
+	sepol_iter_destroy(iter);
+}
+
+int hashtab_add_tests(CU_pSuite suite)
+{
+	if (NULL == CU_add_test(suite, "test_hashtab_iter", test_hashtab_iter)) {
+		return -1;
+	}
+	return 0;
+}
diff -r fceafe45b35c libsepol/tests/test-hashtab.h
--- a/libsepol/tests/test-hashtab.h	Tue Jan 16 17:22:30 2007 -0500
+++ b/libsepol/tests/test-hashtab.h	Tue Jan 16 17:22:40 2007 -0500
@@ -0,0 +1,12 @@
+/* Author: Karl MacMillan <kmacmillan@mentalrootkit.com> */
+
+#ifndef __test_hashtab_h__
+#define __test_hashtab_h__
+
+#include <CUnit/Basic.h>
+
+int hashtab_test_init(void);
+int hashtab_test_cleanup(void);
+int hashtab_add_tests(CU_pSuite suite);
+
+#endif

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC] Add list and iter data types to libsepol
  2007-01-16 21:10 [RFC] Add list and iter data types to libsepol Karl MacMillan
  2007-01-16 22:24 ` Karl MacMillan
@ 2007-01-17 12:15 ` Stephen Smalley
  2007-01-17 15:16   ` Karl MacMillan
  1 sibling, 1 reply; 5+ messages in thread
From: Stephen Smalley @ 2007-01-17 12:15 UTC (permalink / raw)
  To: Karl MacMillan; +Cc: SELinux Mail List

On Tue, 2007-01-16 at 16:10 -0500, Karl MacMillan wrote:
> As part of some work to improve the parser in checkpolicy and module 
> data structures in libsepol, I have implemented doubly-linked list and 
> iterator data types. The attached patch adds these to libsepol.
> 
> I would appreciate feedback on these before I submit larger patches that 
> make use of them. In particular:
> 
> * Is the iterator concept acceptable (I plan to add iterator support to 
> other data types including hashtabs)?

How does this compare with Kevin Carr's previous iterator proposal?  How
does it compare with the existing _iterate functions in libsepol?

> * We've discussed dropping the use of typedefs on structs (which I do in 
> this patch). Is this what we want to do, or should I add typedefs?

Omitting typedefs is preferred I think.

-- 
Stephen Smalley
National Security Agency


--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC] Add list and iter data types to libsepol
  2007-01-17 12:15 ` Stephen Smalley
@ 2007-01-17 15:16   ` Karl MacMillan
  2007-01-18 15:47     ` Stephen Smalley
  0 siblings, 1 reply; 5+ messages in thread
From: Karl MacMillan @ 2007-01-17 15:16 UTC (permalink / raw)
  To: Stephen Smalley; +Cc: SELinux Mail List

Stephen Smalley wrote:
> On Tue, 2007-01-16 at 16:10 -0500, Karl MacMillan wrote:
>> As part of some work to improve the parser in checkpolicy and module 
>> data structures in libsepol, I have implemented doubly-linked list and 
>> iterator data types. The attached patch adds these to libsepol.
>>
>> I would appreciate feedback on these before I submit larger patches that 
>> make use of them. In particular:
>>
>> * Is the iterator concept acceptable (I plan to add iterator support to 
>> other data types including hashtabs)?
> 
> How does this compare with Kevin Carr's previous iterator proposal?

Different implementations and - as far as I remember - different design. 
Same basic idea, though.

>  How
> does it compare with the existing _iterate functions in libsepol?
> 

The iterate functions (which are basically the same as the hashtab 
callbacks) have the same basic goal of allowing external callers to 
iterate over the elements of a data structure without exposing the 
implementation of the data structure. To me, the _iterate functions and 
callbacks have a few problems solved by the iterators:

* Defining callbacks is cumbersome and ofter requires defining data 
structures for passing complex state (see link.c).

* Iterating through nested data structures using callbacks is even more 
painful (see semodule_deps.c for an example of where callbacks would 
have been almost unusable).

* Iterating through multiple, related data structures with the callbacks 
is also difficult or impossible. Think of what the equivalent callbacks 
for the example below would be like (simplified):

int reta, retb;
struct sepol_iter *itera, *iterb;
struct sepol_list *a, *b;

reta = sepol_list_begin(a, itera);
retb = sepol_list_begin(b, iterb);

while (reta == SEPOL_OK && retb == SEPOL_OK) {
	// do something with the data
	sepol_iter_next(itera);
	sepol_iter_next(iterb);
}

* Controlling the traversal (e.g., only visiting every other element or 
stopping traversal) is cumbersome.

* Inserting or removing elements into data structures via callbacks is 
also problematic. At best the performance will be worse. The iterators I 
am proposing can hold whatever state is needed for fast insertion / 
deletion while with the callbacks it is hard to connect the callback 
driver function state (e.g., hashtab_map) with the insertion / deletion 
functions. The hashtab_map_remove_on_error function does work around 
this, but mixing insertion and deletions would be very difficult. See 
test-list.c for an example of how the iterators help with this.

The summary, I think, is that these are a better model overall which is 
why it is a popular approach (at least in non-C languages). Granted, the 
situation is that the callbacks are working for our current needs. So 
I'm not asking for this to be merged without code actually using it 
(which is coming) - just if there are any fundamental objections.

>> * We've discussed dropping the use of typedefs on structs (which I do in 
>> this patch). Is this what we want to do, or should I add typedefs?
> 
> Omitting typedefs is preferred I think.
> 

Ok.

Karl


--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [RFC] Add list and iter data types to libsepol
  2007-01-17 15:16   ` Karl MacMillan
@ 2007-01-18 15:47     ` Stephen Smalley
  0 siblings, 0 replies; 5+ messages in thread
From: Stephen Smalley @ 2007-01-18 15:47 UTC (permalink / raw)
  To: Karl MacMillan; +Cc: SELinux Mail List

On Wed, 2007-01-17 at 10:16 -0500, Karl MacMillan wrote:
> Stephen Smalley wrote:
> > On Tue, 2007-01-16 at 16:10 -0500, Karl MacMillan wrote:
> >> As part of some work to improve the parser in checkpolicy and module 
> >> data structures in libsepol, I have implemented doubly-linked list and 
> >> iterator data types. The attached patch adds these to libsepol.
> >>
> >> I would appreciate feedback on these before I submit larger patches that 
> >> make use of them. In particular:
> >>
> >> * Is the iterator concept acceptable (I plan to add iterator support to 
> >> other data types including hashtabs)?
> > 
> > How does this compare with Kevin Carr's previous iterator proposal?
> 
> Different implementations and - as far as I remember - different design. 
> Same basic idea, though.
> 
> >  How
> > does it compare with the existing _iterate functions in libsepol?
> > 
> 
> The iterate functions (which are basically the same as the hashtab 
> callbacks) have the same basic goal of allowing external callers to 
> iterate over the elements of a data structure without exposing the 
> implementation of the data structure. To me, the _iterate functions and 
> callbacks have a few problems solved by the iterators:
> 
> * Defining callbacks is cumbersome and ofter requires defining data 
> structures for passing complex state (see link.c).
> 
> * Iterating through nested data structures using callbacks is even more 
> painful (see semodule_deps.c for an example of where callbacks would 
> have been almost unusable).
> 
> * Iterating through multiple, related data structures with the callbacks 
> is also difficult or impossible. Think of what the equivalent callbacks 
> for the example below would be like (simplified):
> 
> int reta, retb;
> struct sepol_iter *itera, *iterb;
> struct sepol_list *a, *b;
> 
> reta = sepol_list_begin(a, itera);
> retb = sepol_list_begin(b, iterb);
> 
> while (reta == SEPOL_OK && retb == SEPOL_OK) {
> 	// do something with the data
> 	sepol_iter_next(itera);
> 	sepol_iter_next(iterb);
> }
> 
> * Controlling the traversal (e.g., only visiting every other element or 
> stopping traversal) is cumbersome.
> 
> * Inserting or removing elements into data structures via callbacks is 
> also problematic. At best the performance will be worse. The iterators I 
> am proposing can hold whatever state is needed for fast insertion / 
> deletion while with the callbacks it is hard to connect the callback 
> driver function state (e.g., hashtab_map) with the insertion / deletion 
> functions. The hashtab_map_remove_on_error function does work around 
> this, but mixing insertion and deletions would be very difficult. See 
> test-list.c for an example of how the iterators help with this.
> 
> The summary, I think, is that these are a better model overall which is 
> why it is a popular approach (at least in non-C languages). Granted, the 
> situation is that the callbacks are working for our current needs. So 
> I'm not asking for this to be merged without code actually using it 
> (which is coming) - just if there are any fundamental objections.

No fundamental objection from me.  Just proves that Kevin is always
right ;)

-- 
Stephen Smalley
National Security Agency


--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2007-01-18 15:47 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-01-16 21:10 [RFC] Add list and iter data types to libsepol Karl MacMillan
2007-01-16 22:24 ` Karl MacMillan
2007-01-17 12:15 ` Stephen Smalley
2007-01-17 15:16   ` Karl MacMillan
2007-01-18 15:47     ` Stephen Smalley

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.