* Security Attribute Functions
*/
+#define _CM_F_NONE 0x00000000
+#define _CM_F_ALLOC 0x00000001
+
+/**
+ * _netlbl_secattr_catmap_getnode - Get a individual node from a catmap
+ * @catmap: pointer to the category bitmap
+ * @offset: the requested offset
+ * @cm_flags: catmap flags, see _CM_F_*
+ * @gfp_flags: memory allocation flags
+ *
+ * Description:
+ * Iterate through the catmap looking for the node associated with @offset; if
+ * the _CM_F_ALLOC flag is set in @cm_flags and there is no associated node,
+ * one will be created and inserted into the catmap. Returns a pointer to the
+ * node on success, NULL on failure.
+ *
+ */
+static struct netlbl_lsm_secattr_catmap *_netlbl_secattr_catmap_getnode(
+ struct netlbl_lsm_secattr_catmap **catmap,
+ u32 offset,
+ unsigned int cm_flags,
+ gfp_t gfp_flags)
+{
+ struct netlbl_lsm_secattr_catmap *iter = *catmap;
+ struct netlbl_lsm_secattr_catmap *prev = NULL;
+
+ if (iter == NULL || offset < iter->startbit)
+ goto secattr_catmap_getnode_alloc;
+ while (iter && offset >= (iter->startbit + NETLBL_CATMAP_SIZE)) {
+ prev = iter;
+ iter = iter->next;
+ }
+ if (iter == NULL || offset < iter->startbit)
+ goto secattr_catmap_getnode_alloc;
+
+ return iter;
+
+secattr_catmap_getnode_alloc:
+ if (!(cm_flags & _CM_F_ALLOC))
+ return NULL;
+
+ iter = netlbl_secattr_catmap_alloc(gfp_flags);
+ if (iter == NULL)
+ return NULL;
+ iter->startbit = offset & ~(NETLBL_CATMAP_SIZE - 1);
+
+ if (prev == NULL) {
+ iter->next = *catmap;
+ *catmap = iter;
+ } else {
+ iter->next = prev->next;
+ prev->next = iter;
+ }
+
+ return iter;
+}
+
/**
* netlbl_secattr_catmap_walk - Walk a LSM secattr catmap looking for a bit
* @catmap: the category bitmap
return -ENOENT;
}
+/**
+ * netlbl_secattr_catmap_getlong - Export an unsigned long bitmap
+ * @catmap: pointer to the category bitmap
+ * @offset: pointer to the requested offset
+ * @bitmap: the exported bitmap
+ *
+ * Description:
+ * Export a bitmap with an offset greater than or equal to @offset and return
+ * it in @bitmap. The @offset must be aligned to an unsigned long and will be
+ * updated on return if different from what was requested; if the catmap is
+ * empty at the requested offset and beyond, the @offset is set to (u32)-1.
+ * Returns zero on sucess, negative values on failure.
+ *
+ */
+int netlbl_secattr_catmap_getlong(struct netlbl_lsm_secattr_catmap *catmap,
+ u32 *offset,
+ unsigned long *bitmap)
+{
+ struct netlbl_lsm_secattr_catmap *iter;
+ u32 off = *offset;
+ u32 idx;
+
+ /* only allow aligned offsets */
+ if ((off & (BITS_PER_LONG - 1)) != 0)
+ return -EINVAL;
+
+ if (off < catmap->startbit) {
+ off = catmap->startbit;
+ *offset = off;
+ }
+ iter = _netlbl_secattr_catmap_getnode(&catmap, off, _CM_F_NONE, 0);
+ if (iter == NULL) {
+ *offset = (u32)-1;
+ return 0;
+ }
+
+ if (off < iter->startbit) {
+ off = iter->startbit;
+ *offset = off;
+ } else
+ off -= iter->startbit;
+
+ idx = off / NETLBL_CATMAP_MAPSIZE;
+ *bitmap = iter->bitmap[idx] >> (off % NETLBL_CATMAP_SIZE);
+
+ return 0;
+}
+
/**
* netlbl_secattr_catmap_setbit - Set a bit in a LSM secattr catmap
* @catmap: pointer to the category bitmap
u32 bit,
gfp_t flags)
{
- struct netlbl_lsm_secattr_catmap *iter = *catmap;
- u32 node_bit;
- u32 node_idx;
+ struct netlbl_lsm_secattr_catmap *iter;
+ u32 idx;
- while (iter->next != NULL &&
- bit >= (iter->startbit + NETLBL_CATMAP_SIZE))
- iter = iter->next;
- if (bit < iter->startbit) {
- iter = netlbl_secattr_catmap_alloc(flags);
- if (iter == NULL)
- return -ENOMEM;
- iter->next = *catmap;
- iter->startbit = bit & ~(NETLBL_CATMAP_SIZE - 1);
- *catmap = iter;
- } else if (bit >= (iter->startbit + NETLBL_CATMAP_SIZE)) {
- iter->next = netlbl_secattr_catmap_alloc(flags);
- if (iter->next == NULL)
- return -ENOMEM;
- iter = iter->next;
- iter->startbit = bit & ~(NETLBL_CATMAP_SIZE - 1);
- }
+ iter = _netlbl_secattr_catmap_getnode(catmap, bit, _CM_F_ALLOC, flags);
+ if (iter == NULL)
+ return -ENOMEM;
- /* gcc always rounds to zero when doing integer division */
- node_idx = (bit - iter->startbit) / NETLBL_CATMAP_MAPSIZE;
- node_bit = bit - iter->startbit - (NETLBL_CATMAP_MAPSIZE * node_idx);
- iter->bitmap[node_idx] |= NETLBL_CATMAP_BIT << node_bit;
+ bit -= iter->startbit;
+ idx = bit / NETLBL_CATMAP_MAPSIZE;
+ iter->bitmap[idx] |= NETLBL_CATMAP_BIT << (bit % NETLBL_CATMAP_MAPSIZE);
return 0;
}
u32 end,
gfp_t flags)
{
- int ret_val = 0;
- struct netlbl_lsm_secattr_catmap *iter = *catmap;
- u32 iter_max_spot;
- u32 spot;
- u32 orig_spot = iter->startbit;
-
- /* XXX - This could probably be made a bit faster by combining writes
- * to the catmap instead of setting a single bit each time, but for
- * right now skipping to the start of the range in the catmap should
- * be a nice improvement over calling the individual setbit function
- * repeatedly from a loop. */
-
- while (iter->next != NULL &&
- start >= (iter->startbit + NETLBL_CATMAP_SIZE))
- iter = iter->next;
- iter_max_spot = iter->startbit + NETLBL_CATMAP_SIZE;
-
- for (spot = start; spot <= end && ret_val == 0; spot++) {
- if (spot >= iter_max_spot && iter->next != NULL) {
- iter = iter->next;
- iter_max_spot = iter->startbit + NETLBL_CATMAP_SIZE;
- }
- ret_val = netlbl_secattr_catmap_setbit(&iter, spot, flags);
- if (iter->startbit < orig_spot)
- *catmap = iter;
+ int rc = 0;
+ u32 spot = start;
+
+ while (rc == 0 && spot <= end) {
+ if (((spot & (BITS_PER_LONG - 1)) != 0) &&
+ ((end - spot) > BITS_PER_LONG)) {
+ rc = netlbl_secattr_catmap_setlong(catmap,
+ spot,
+ (unsigned long)-1,
+ flags);
+ spot += BITS_PER_LONG;
+ } else
+ rc = netlbl_secattr_catmap_setbit(catmap,
+ spot++,
+ flags);
}
- return ret_val;
+ return rc;
+}
+
+/**
+ * netlbl_secattr_catmap_setlong - Import an unsigned long bitmap
+ * @catmap: pointer to the category bitmap
+ * @offset: offset to the start of the imported bitmap
+ * @bitmap: the bitmap to import
+ * @flags: memory allocation flags
+ *
+ * Description:
+ * Import the bitmap specified in @bitmap into @catmap, using the offset
+ * in @offset. The offset must be aligned to an unsigned long. Returns zero
+ * on success, negative values on failure.
+ *
+ */
+int netlbl_secattr_catmap_setlong(struct netlbl_lsm_secattr_catmap **catmap,
+ u32 offset,
+ unsigned long bitmap,
+ gfp_t flags)
+{
+ struct netlbl_lsm_secattr_catmap *iter;
+ u32 idx;
+
+ /* only allow aligned offsets */
+ if ((offset & (BITS_PER_LONG - 1)) != 0)
+ return -EINVAL;
+
+ iter = _netlbl_secattr_catmap_getnode(catmap,
+ offset, _CM_F_ALLOC, flags);
+ if (iter == NULL)
+ return -ENOMEM;
+
+ offset -= iter->startbit;
+ idx = offset / NETLBL_CATMAP_MAPSIZE;
+ iter->bitmap[idx] |= bitmap << (offset % NETLBL_CATMAP_MAPSIZE);
+
+ return 0;
}
/*
struct netlbl_lsm_secattr_catmap **catmap)
{
struct ebitmap_node *e_iter = ebmap->node;
- struct netlbl_lsm_secattr_catmap *c_iter;
- u32 cmap_idx, cmap_sft;
- int i;
-
- /* NetLabel's NETLBL_CATMAP_MAPTYPE is defined as an array of u64,
- * however, it is not always compatible with an array of unsigned long
- * in ebitmap_node.
- * In addition, you should pay attention the following implementation
- * assumes unsigned long has a width equal with or less than 64-bit.
- */
+ unsigned long e_map;
+ u32 offset;
+ unsigned int iter;
+ int rc;
if (e_iter == NULL) {
*catmap = NULL;
return 0;
}
- c_iter = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
- if (c_iter == NULL)
- return -ENOMEM;
- *catmap = c_iter;
- c_iter->startbit = e_iter->startbit & ~(NETLBL_CATMAP_SIZE - 1);
+ if (*catmap != NULL)
+ netlbl_secattr_catmap_free(*catmap);
+ *catmap = NULL;
while (e_iter) {
- for (i = 0; i < EBITMAP_UNIT_NUMS; i++) {
- unsigned int delta, e_startbit, c_endbit;
-
- e_startbit = e_iter->startbit + i * EBITMAP_UNIT_SIZE;
- c_endbit = c_iter->startbit + NETLBL_CATMAP_SIZE;
- if (e_startbit >= c_endbit) {
- c_iter->next
- = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
- if (c_iter->next == NULL)
+ offset = e_iter->startbit;
+ for (iter = 0; iter < EBITMAP_UNIT_NUMS; iter++) {
+ e_map = e_iter->maps[iter];
+ if (e_map != 0) {
+ rc = netlbl_secattr_catmap_setlong(catmap,
+ offset,
+ e_map,
+ GFP_ATOMIC);
+ if (rc != 0)
goto netlbl_export_failure;
- c_iter = c_iter->next;
- c_iter->startbit
- = e_startbit & ~(NETLBL_CATMAP_SIZE - 1);
}
- delta = e_startbit - c_iter->startbit;
- cmap_idx = delta / NETLBL_CATMAP_MAPSIZE;
- cmap_sft = delta % NETLBL_CATMAP_MAPSIZE;
- c_iter->bitmap[cmap_idx]
- |= e_iter->maps[i] << cmap_sft;
+ offset += EBITMAP_UNIT_SIZE;
}
e_iter = e_iter->next;
}
int ebitmap_netlbl_import(struct ebitmap *ebmap,
struct netlbl_lsm_secattr_catmap *catmap)
{
+ int rc;
struct ebitmap_node *e_iter = NULL;
- struct ebitmap_node *emap_prev = NULL;
- struct netlbl_lsm_secattr_catmap *c_iter = catmap;
- u32 c_idx, c_pos, e_idx, e_sft;
-
- /* NetLabel's NETLBL_CATMAP_MAPTYPE is defined as an array of u64,
- * however, it is not always compatible with an array of unsigned long
- * in ebitmap_node.
- * In addition, you should pay attention the following implementation
- * assumes unsigned long has a width equal with or less than 64-bit.
- */
-
- do {
- for (c_idx = 0; c_idx < NETLBL_CATMAP_MAPCNT; c_idx++) {
- unsigned int delta;
- u64 map = c_iter->bitmap[c_idx];
-
- if (!map)
- continue;
+ struct ebitmap_node *e_prev = NULL;
+ u32 offset = 0, idx;
+ unsigned long bitmap;
+
+ for (;;) {
+ rc = netlbl_secattr_catmap_getlong(catmap, &offset, &bitmap);
+ if (rc < 0)
+ goto netlbl_import_failure;
+ if (offset == (u32)-1)
+ return 0;
- c_pos = c_iter->startbit
- + c_idx * NETLBL_CATMAP_MAPSIZE;
- if (!e_iter
- || c_pos >= e_iter->startbit + EBITMAP_SIZE) {
- e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC);
- if (!e_iter)
- goto netlbl_import_failure;
- e_iter->startbit
- = c_pos - (c_pos % EBITMAP_SIZE);
- if (emap_prev == NULL)
- ebmap->node = e_iter;
- else
- emap_prev->next = e_iter;
- emap_prev = e_iter;
- }
- delta = c_pos - e_iter->startbit;
- e_idx = delta / EBITMAP_UNIT_SIZE;
- e_sft = delta % EBITMAP_UNIT_SIZE;
- while (map) {
- e_iter->maps[e_idx++] |= map & (-1UL);
- map = EBITMAP_SHIFT_UNIT_SIZE(map);
- }
+ if (e_iter == NULL ||
+ offset >= e_iter->startbit + EBITMAP_SIZE) {
+ e_prev = e_iter;
+ e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC);
+ if (e_iter == NULL)
+ goto netlbl_import_failure;
+ e_iter->startbit = offset & ~(EBITMAP_SIZE - 1);
+ if (e_prev == NULL)
+ ebmap->node = e_iter;
+ else
+ e_prev->next = e_iter;
+ ebmap->highbit = e_iter->startbit + EBITMAP_SIZE;
}
- c_iter = c_iter->next;
- } while (c_iter);
- if (e_iter != NULL)
- ebmap->highbit = e_iter->startbit + EBITMAP_SIZE;
- else
- ebitmap_destroy(ebmap);
+ /* offset will always be aligned to an unsigned long */
+ idx = EBITMAP_NODE_INDEX(e_iter, offset);
+ e_iter->maps[idx] = bitmap;
+
+ /* next */
+ offset += EBITMAP_UNIT_SIZE;
+ }
+
+ /* NOTE: we should never reach this return */
return 0;
netlbl_import_failure: