aboutsummaryrefslogtreecommitdiff
path: root/libshouldbeinlibc/idvec.c
diff options
context:
space:
mode:
Diffstat (limited to 'libshouldbeinlibc/idvec.c')
-rw-r--r--libshouldbeinlibc/idvec.c338
1 files changed, 338 insertions, 0 deletions
diff --git a/libshouldbeinlibc/idvec.c b/libshouldbeinlibc/idvec.c
new file mode 100644
index 00000000..692c4788
--- /dev/null
+++ b/libshouldbeinlibc/idvec.c
@@ -0,0 +1,338 @@
+/* Routines for vectors of uids/gids
+
+ Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <malloc.h>
+#include <string.h>
+#include <idvec.h>
+
+/* Return a new, empty, idvec, or NULL if there wasn't enough memory. */
+struct idvec *
+make_idvec ()
+{
+ struct idvec *idvec = malloc (sizeof (struct idvec));
+ if (idvec)
+ {
+ idvec->alloced = idvec->num = 0;
+ idvec->ids = 0;
+ }
+ return idvec;
+}
+
+/* Free's IDVEC, but not the storage pointed to by the IDS field. */
+void
+idvec_free_wrapper (struct idvec *idvec)
+{
+ free (idvec);
+}
+
+void
+idvec_free_contents (struct idvec *idvec)
+{
+ if (idvec->alloced)
+ free (idvec->ids);
+}
+
+void
+idvec_free (struct idvec *idvec)
+{
+ idvec_free_contents (idvec);
+ idvec_free_wrapper (idvec);
+}
+
+/* Ensure that IDVEC has enough spaced allocated to hold NUM ids, thus
+ ensuring that any subsequent ids added won't return a memory allocation
+ error unless it would result in more ids that NUM. ENOMEM is returned if
+ a memory allocation error occurs. */
+error_t
+idvec_ensure (struct idvec *idvec, unsigned num)
+{
+ if (num > idvec->alloced)
+ {
+ uid_t *_ids = realloc (idvec->ids, num * sizeof (uid_t));
+ if (! _ids)
+ return ENOMEM;
+ idvec->ids = _ids;
+ idvec->alloced = num;
+ }
+ return 0;
+}
+
+/* Like idvec_ensure(), but takes INC, the increment of the number of ids
+ already in IDVEC as an argument. */
+error_t
+idvec_grow (struct idvec *idvec, unsigned inc)
+{
+ return idvec_ensure (idvec, idvec->num + inc);
+}
+
+/* Returns true if IDVEC contains ID, at or after position POS. */
+int
+idvec_tail_contains (const struct idvec *idvec, unsigned pos, uid_t id)
+{
+ uid_t *ids = idvec->ids, *end = ids + idvec->num, *p = ids + pos;
+ while (p < end)
+ if (*p++ == id)
+ return 1;
+ return 0;
+}
+
+/* Insert ID into IDVEC at position POS, returning ENOMEM if there wasn't
+ enough memory, or 0. */
+error_t
+idvec_insert (struct idvec *idvec, unsigned pos, uid_t id)
+{
+ error_t err = 0;
+ unsigned num = idvec->num;
+ unsigned new_num = (pos < num ? num + 1 : pos + 1);
+
+ if (idvec->alloced == num)
+ /* If we seem to be growing by just one, actually prealloc some more. */
+ err = idvec_ensure (idvec, new_num + num);
+ else
+ err = idvec_ensure (idvec, new_num);
+
+ if (! err)
+ {
+ uid_t *ids = idvec->ids;
+ if (pos < num)
+ bcopy (ids + pos, ids + pos + 1, (num - pos) * sizeof (uid_t));
+ else if (pos > num)
+ bzero (ids + num, (pos - num) * sizeof (uid_t));
+ ids[pos] = id;
+ idvec->num = new_num;
+ }
+
+ return err;
+}
+
+/* Add ID onto the end of IDVEC, returning ENOMEM if there's not enough memory,
+ or 0. */
+error_t
+idvec_add (struct idvec *idvec, uid_t id)
+{
+ return idvec_insert (idvec, idvec->num, id);
+}
+
+/* If IDVEC doesn't contain ID, add it onto the end, returning ENOMEM if
+ there's not enough memory; otherwise, do nothing. */
+error_t
+idvec_add_new (struct idvec *idvec, uid_t id)
+{
+ if (idvec_contains (idvec, id))
+ return 0;
+ else
+ return idvec_add (idvec, id);
+}
+
+/* If IDVEC doesn't contain ID at position POS or after, insert it at POS,
+ returning ENOMEM if there's not enough memory; otherwise, do nothing. */
+error_t
+idvec_insert_new (struct idvec *idvec, unsigned pos, uid_t id)
+{
+ if (idvec_tail_contains (idvec, pos, id))
+ return 0;
+ else
+ return idvec_insert (idvec, pos, id);
+}
+
+/* Set the ids in IDVEC to IDS (NUM elements long); delete whatever
+ the previous ids were. */
+error_t
+idvec_set_ids (struct idvec *idvec, const uid_t *ids, unsigned num)
+{
+ error_t err;
+
+ err = idvec_ensure (idvec, num);
+ if (!err)
+ {
+ bcopy (ids, idvec->ids, num * sizeof (uid_t));
+ idvec->num = num;
+ }
+ return err;
+}
+
+/* Like idvec_set_ids, but get the new ids from new. */
+error_t
+idvec_set (struct idvec *idvec, const struct idvec *new)
+{
+ return idvec_set_ids (idvec, new->ids, new->num);
+}
+
+/* Adds each id in the vector IDS (NUM elements long) to IDVEC, as long as it
+ wasn't previously in IDVEC. */
+error_t
+idvec_merge_ids (struct idvec *idvec, const uid_t *ids, unsigned num)
+{
+ error_t err = 0;
+ unsigned num_old = idvec->num;
+ while (num-- > 0 && !err)
+ {
+ int i;
+ for (i = 0; i < num_old; i++)
+ if (idvec->ids[i] == *ids)
+ break;
+ if (i == num_old)
+ err = idvec_add (idvec, *ids);
+ ids++;
+ }
+ return err;
+}
+
+/* Adds each id from NEW to IDVEC, as if with idvec_add_new(). */
+error_t
+idvec_merge (struct idvec *idvec, const struct idvec *new)
+{
+ return idvec_merge_ids (idvec, new->ids, new->num);
+}
+
+/* Remove any occurances of ID in IDVEC after position POS.
+ Returns true if anything was done. */
+int
+idvec_remove (struct idvec *idvec, unsigned pos, uid_t id)
+{
+ if (pos < idvec->num)
+ {
+ int left = idvec->num - pos;
+ uid_t *ids = idvec->ids + pos, *targ = ids;
+ while (left--)
+ {
+ if (*ids != id)
+ {
+ if (ids != targ)
+ *targ = *ids;
+ targ++;
+ }
+ ids++;
+ }
+ if (ids == targ)
+ return 0;
+ idvec->num = targ - idvec->ids;
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/* Remove all ids in SUB from IDVEC, returning true if anything was done. */
+int
+idvec_subtract (struct idvec *idvec, const struct idvec *sub)
+{
+ int i;
+ int done = 0;
+ for (i = 0; i < sub->num; i++)
+ done |= idvec_remove (idvec, 0, sub->ids[i]);
+ return done;
+}
+
+/* Remove all ids from IDVEC that are *not* in KEEP, returning true if
+ anything was changed. */
+int
+idvec_keep (struct idvec *idvec, const struct idvec *keep)
+{
+ uid_t *old = idvec->ids, *new = old, *end = old + idvec->num;
+
+ while (old < end)
+ {
+ uid_t id = *old++;
+ if (idvec_contains (keep, id))
+ {
+ if (old != new)
+ *new = id;
+ new++;
+ }
+ }
+
+ if (old != new)
+ {
+ idvec->num = new - idvec->ids;
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/* Deleted the id at position POS in IDVEC. */
+void
+idvec_delete (struct idvec *idvec, unsigned pos)
+{
+ unsigned num = idvec->num;
+ if (pos < num)
+ {
+ uid_t *ids = idvec->ids;
+ idvec->num = --num;
+ if (num > pos)
+ bcopy (ids + pos + 1, ids + pos, (num - pos) * sizeof (uid_t));
+ }
+}
+
+/* Insert ID at position POS in IDVEC, remove any instances of ID previously
+ present at POS or after. ENOMEM is returned if there's not enough memory,
+ otherwise 0. */
+error_t
+idvec_insert_only (struct idvec *idvec, unsigned pos, uid_t id)
+{
+ if (idvec->num > pos && idvec->ids[pos] == id)
+ return 0;
+ else
+ {
+ idvec_remove (idvec, pos, id);
+ return idvec_insert (idvec, pos, id);
+ }
+}
+
+/* EFF and AVAIL should be idvec's corresponding to a processes
+ effective and available ids. ID replaces the first id in EFF, and,
+ if there are any IDs in AVAIL, replaces the second ID in AVAIL;
+ what it replaces in any case is preserved by adding it to AVAIL if
+ not already present. In addition, the If SECURE is non-NULL, and
+ ID was not previously present in either EFF or AVAIL, then *SECURE
+ is set to true. ENOMEM is returned if a malloc fails, otherwise 0.
+ The return parameters are only touched if this call succeeds. */
+error_t
+idvec_setid (struct idvec *eff, struct idvec *avail, uid_t id, int *secure)
+{
+ error_t err;
+ /* True if ID was not previously present in either EFF or AVAIL. */
+ int _secure = !idvec_contains (eff, id) && !idvec_contains (avail, id);
+
+ if (eff->num > 0)
+ {
+ /* If there are any old effective ids, we replace eff[0] with
+ ID, and try to preserve the old eff[0] by putting it in AVAIL
+ list if necessary. */
+ err = idvec_add_new (avail, eff->ids[0]);
+ if (!err)
+ eff->ids[0] = id;
+ }
+ else
+ /* No previous effective ids, just make ID the first one. */
+ err = idvec_add (eff, id);
+
+ if (avail->num > 0 && !err)
+ err = idvec_insert_only (avail, 1, id);
+
+ if (err)
+ return err;
+
+ if (_secure && secure && !*secure)
+ *secure = 1;
+
+ return 0;
+}