diff options
Diffstat (limited to 'libshouldbeinlibc/idvec.c')
-rw-r--r-- | libshouldbeinlibc/idvec.c | 338 |
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; +} |