e671f95d32a3efee808b983e7f6d7bb0c7d8e155
[openafs.git] / src / libadmin / pts / afs_ptsAdmin.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13
14 #include <stdio.h>
15 #include <ctype.h>
16 #include <afs/stds.h>
17 #include <rx/rx.h>
18 #include <rx/rxstat.h>
19 #include "afs_ptsAdmin.h"
20 #include "../adminutil/afs_AdminInternal.h"
21 #include <afs/afs_AdminErrors.h>
22 #include <afs/afs_utilAdmin.h>
23 #include <afs/ptint.h>
24 #include <afs/ptserver.h>
25
26 /*
27  * IsValidCellHandle - validate the cell handle for making pts
28  * requests.
29  *
30  * PARAMETERS
31  *
32  * IN cellHandle - a previously opened cellHandle that is to be validated.
33  *
34  * LOCKS
35  *
36  * No locks are obtained or released by this function
37  *
38  * RETURN CODES
39  *
40  * Returns != 0 upon successful completion.
41  *
42  */
43
44 static int
45 IsValidCellHandle(const afs_cell_handle_p c_handle, afs_status_p st)
46 {
47     int rc = 0;
48     afs_status_t tst = 0;
49
50     if (!CellHandleIsValid((void *)c_handle, &tst)) {
51         goto fail_IsValidCellHandle;
52     }
53
54     if (c_handle->pts_valid == 0) {
55         tst = ADMCLIENTCELLPTSINVALID;
56         goto fail_IsValidCellHandle;
57     }
58
59     if (c_handle->pts == NULL) {
60         tst = ADMCLIENTCELLPTSNULL;
61         goto fail_IsValidCellHandle;
62     }
63     rc = 1;
64
65
66   fail_IsValidCellHandle:
67
68     if (st != NULL) {
69         *st = tst;
70     }
71     return rc;
72 }
73
74
75 /*
76  * TranslatePTSNames - translate character representations of pts names
77  * into their numeric equivalent.
78  *
79  * PARAMETERS
80  *
81  * IN cellHandle - a previously opened cellHandle that corresponds
82  * to the cell where the id's exist.
83  *
84  * IN names - the list of names to be translated.
85  *
86  * OUT ids - the list of translated names
87  *
88  * LOCKS
89  *
90  * No locks are obtained or released by this function
91  *
92  * RETURN CODES
93  *
94  * Returns != 0 upon successful completion.
95  *
96  */
97
98 static int
99 TranslatePTSNames(const afs_cell_handle_p cellHandle, namelist * names,
100                   idlist * ids, afs_status_p st)
101 {
102     int rc = 0;
103     afs_status_t tst = 0;
104     int i;
105     char *p;
106
107     /*
108      * Lowercase the names to translate
109      */
110
111     for (i = 0; i < names->namelist_len; i++) {
112         p = names->namelist_val[i];
113         while (*p) {
114             *p = tolower(*p);
115             p++;
116         }
117     }
118
119     tst = ubik_PR_NameToID(cellHandle->pts, 0, names, ids);
120
121     if (tst) {
122         goto fail_TranslatePTSNames;
123     }
124
125
126     /*
127      * Check to see if the lookup failed
128      */
129
130     for (i = 0; i < ids->idlist_len; i++) {
131         if (ids->idlist_val[i] == ANONYMOUSID) {
132             tst = ADMPTSFAILEDNAMETRANSLATE;
133             goto fail_TranslatePTSNames;
134         }
135     }
136     rc = 1;
137
138   fail_TranslatePTSNames:
139
140     if (st != NULL) {
141         *st = tst;
142     }
143     return rc;
144 }
145
146 /*
147  * TranslateTwoNames - translate two pts names to their pts ids.
148  *
149  * PARAMETERS
150  *
151  * IN cellHandle - a previously opened cellHandle that corresponds
152  * to the cell where the group exists.
153  *
154  * IN id1 - one id to be translated
155  *
156  * IN error1 - the error status to be returned in the event that id1 is
157  * too long.
158  *
159  * IN id2 - one id to be translated
160  *
161  * IN error2 - the error status to be returned in the event that id2 is
162  * too long.
163  *
164  *
165  * OUT idlist - the list of pts id's
166  *
167  * LOCKS
168  *
169  * No locks are obtained or released by this function
170  *
171  * RETURN CODES
172  *
173  * Returns != 0 upon successful completion.
174  *
175  */
176
177 static int
178 TranslateTwoNames(const afs_cell_handle_p c_handle, const char *id1,
179                   afs_status_t error1, const char *id2, afs_status_t error2,
180                   idlist * ids, afs_status_p st)
181 {
182     int rc = 0;
183     afs_status_t tst = 0;
184     namelist names;
185     char tmp_array[2 * PTS_MAX_NAME_LEN];
186
187     /*
188      * Copy the group and user names in order to translate them
189      */
190
191     names.namelist_len = 2;
192     names.namelist_val = (prname *) & tmp_array[0];
193
194     strncpy(names.namelist_val[0], id1, PTS_MAX_NAME_LEN);
195     names.namelist_val[0][PTS_MAX_NAME_LEN - 1] = '\0';
196     strncpy(names.namelist_val[1], id2, PTS_MAX_NAME_LEN);
197     names.namelist_val[1][PTS_MAX_NAME_LEN - 1] = '\0';
198     ids->idlist_val = 0;
199     ids->idlist_len = 0;
200
201     /*
202      * Check that user and group aren't too long
203      * This is a cheaper check than calling strlen
204      */
205
206     if (names.namelist_val[0][PTS_MAX_NAME_LEN - 1] != 0) {
207         tst = error1;
208         goto fail_TranslateTwoNames;
209     }
210
211     if (names.namelist_val[0][PTS_MAX_NAME_LEN - 1] != 0) {
212         tst = error2;
213         goto fail_TranslateTwoNames;
214     }
215
216     /*
217      * Translate user and group into pts ID's
218      */
219
220     if (TranslatePTSNames(c_handle, &names, ids, &tst) == 0) {
221         goto fail_TranslateTwoNames;
222     }
223     rc = 1;
224
225
226   fail_TranslateTwoNames:
227
228     if (st != NULL) {
229         *st = tst;
230     }
231     return rc;
232 }
233
234 /*
235  * TranslateOneName - translate a pts name to its pts id.
236  *
237  * PARAMETERS
238  *
239  * IN cellHandle - a previously opened cellHandle that corresponds
240  * to the cell where the group exists.
241  *
242  * IN userName - the user to be translated.
243  *
244  * OUT idlist - the user pts id.
245  *
246  * LOCKS
247  *
248  * No locks are obtained or released by this function
249  *
250  * RETURN CODES
251  *
252  * Returns != 0 upon successful completion.
253  *
254  */
255
256 static int
257 TranslateOneName(const afs_cell_handle_p c_handle, const char *ptsName,
258                  afs_status_t tooLongError, afs_int32 * ptsId,
259                  afs_status_p st)
260 {
261     int rc = 0;
262     afs_status_t tst = 0;
263     namelist names[1];
264     char tmp_array[PTS_MAX_NAME_LEN];
265     idlist ids;
266
267     /*
268      * Copy the name in order to translate it
269      */
270
271     names[0].namelist_len = 1;
272     names[0].namelist_val = (prname *) & tmp_array[0];
273
274     strncpy((char *)names[0].namelist_val, ptsName, PTS_MAX_NAME_LEN);
275     ((char *)names[0].namelist_val)[PTS_MAX_NAME_LEN - 1] = '\0';
276     ids.idlist_val = 0;
277     ids.idlist_len = 0;
278
279     /*
280      * Check that user isn't too long
281      * This is a cheaper check than calling strlen
282      */
283
284     if (names[0].namelist_val[0][PTS_MAX_NAME_LEN - 1] != 0) {
285         tst = tooLongError;
286         goto fail_TranslateOneName;
287     }
288
289     /*
290      * Translate user into pts ID
291      */
292
293     if (TranslatePTSNames(c_handle, names, &ids, &tst) == 0) {
294         goto fail_TranslateOneName;
295     } else {
296         if (ids.idlist_val != NULL) {
297             *ptsId = *ids.idlist_val;
298             free(ids.idlist_val);
299         }
300     }
301     rc = 1;
302
303
304   fail_TranslateOneName:
305
306     if (st != NULL) {
307         *st = tst;
308     }
309     return rc;
310 }
311
312 /*
313  * TranslatePTSIds - translate numeric representations of pts names
314  * into their character equivalent.
315  *
316  * PARAMETERS
317  *
318  * IN cellHandle - a previously opened cellHandle that corresponds
319  * to the cell where the id's exist.
320  *
321  * IN ids - the list of ids to be translated.
322  *
323  * OUT names - the list of translated names
324  *
325  * LOCKS
326  *
327  * No locks are obtained or released by this function
328  *
329  * RETURN CODES
330  *
331  * Returns != 0 upon successful completion.
332  *
333  */
334
335 static int
336 TranslatePTSIds(const afs_cell_handle_p cellHandle, namelist * names,
337                 idlist * ids, afs_status_p st)
338 {
339     int rc = 0;
340     afs_status_t tst = 0;
341
342     tst = ubik_PR_IDToName(cellHandle->pts, 0, ids, names);
343
344     if (tst) {
345         goto fail_TranslatePTSIds;
346     }
347     rc = 1;
348
349   fail_TranslatePTSIds:
350
351     if (st != NULL) {
352         *st = tst;
353     }
354     return rc;
355 }
356
357 /*
358  * pts_GroupMemberAdd - add one member to a pts group
359  *
360  * PARAMETERS
361  *
362  * IN cellHandle - a previously opened cellHandle that corresponds
363  * to the cell where the group exists.
364  *
365  * IN userName - the name to be added to the group.
366  *
367  * IN groupName - the group to be modified.
368  *
369  * LOCKS
370  *
371  * No locks are obtained or released by this function
372  *
373  * RETURN CODES
374  *
375  * Returns != 0 upon successful completion.
376  *
377  */
378
379 int ADMINAPI
380 pts_GroupMemberAdd(const void *cellHandle, const char *userName,
381                    const char *groupName, afs_status_p st)
382 {
383     int rc = 0;
384     afs_status_t tst = 0;
385     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
386         idlist ids = {0,0};
387
388     /*
389      * Validate arguments
390      */
391
392     if (!IsValidCellHandle(c_handle, &tst)) {
393         goto fail_pts_GroupMemberAdd;
394     }
395
396     if ((userName == NULL) || (*userName == 0)) {
397         tst = ADMPTSUSERNAMENULL;
398         goto fail_pts_GroupMemberAdd;
399     }
400
401     if ((groupName == NULL) || (*groupName == 0)) {
402         tst = ADMPTSGROUPNAMENULL;
403         goto fail_pts_GroupMemberAdd;
404     }
405
406     if (!TranslateTwoNames
407         (c_handle, userName, ADMPTSUSERNAMETOOLONG, groupName,
408          ADMPTSGROUPNAMETOOLONG, &ids, &tst)) {
409         goto fail_pts_GroupMemberAdd;
410     }
411
412     /*
413      * Make the rpc
414      */
415
416     tst =
417         ubik_PR_AddToGroup(c_handle->pts, 0, ids.idlist_val[0],
418                   ids.idlist_val[1]);
419
420     if (tst != 0) {
421         goto fail_pts_GroupMemberAdd;
422     }
423     rc = 1;
424
425   fail_pts_GroupMemberAdd:
426
427     if (ids.idlist_val != 0) {
428         free(ids.idlist_val);
429     }
430
431     if (st != NULL) {
432         *st = tst;
433     }
434     return rc;
435 }
436
437 /*
438  * pts_GroupOwnerChange - change the owner of a group
439  *
440  * PARAMETERS
441  *
442  * IN cellHandle - a previously opened cellHandle that corresponds
443  * to the cell where the group exists.
444  *
445  * IN targetGroup - the group to be modified.
446  *
447  * IN userName - the new owner of the group.
448  *
449  * LOCKS
450  *
451  * No locks are obtained or released by this function
452  *
453  * RETURN CODES
454  *
455  * Returns != 0 upon successful completion.
456  *
457  */
458
459 int ADMINAPI
460 pts_GroupOwnerChange(const void *cellHandle, const char *targetGroup,
461                      const char *newOwner, afs_status_p st)
462 {
463     int rc = 0;
464     afs_status_t tst = 0;
465     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
466     idlist ids;
467
468     /*
469      * Validate arguments
470      */
471
472     if (!IsValidCellHandle(c_handle, &tst)) {
473         goto fail_pts_GroupOwnerChange;
474     }
475
476     if ((newOwner == NULL) || (*newOwner == 0)) {
477         tst = ADMPTSNEWOWNERNULL;
478         goto fail_pts_GroupOwnerChange;
479     }
480
481     if ((targetGroup == NULL) || (*targetGroup == 0)) {
482         tst = ADMPTSTARGETGROUPNULL;
483         goto fail_pts_GroupOwnerChange;
484     }
485
486     if (!TranslateTwoNames
487         (c_handle, newOwner, ADMPTSNEWOWNERTOOLONG, targetGroup,
488          ADMPTSTARGETGROUPTOOLONG, &ids, &tst)) {
489         goto fail_pts_GroupOwnerChange;
490     }
491
492     /*
493      * Make the rpc
494      */
495
496     tst =
497         ubik_PR_ChangeEntry(c_handle->pts, 0, ids.idlist_val[1], "",
498                   ids.idlist_val[0], 0);
499
500     if (tst != 0) {
501         goto fail_pts_GroupOwnerChange;
502     }
503     rc = 1;
504
505   fail_pts_GroupOwnerChange:
506
507     if (ids.idlist_val != 0) {
508         free(ids.idlist_val);
509     }
510
511     if (st != NULL) {
512         *st = tst;
513     }
514     return rc;
515 }
516
517 /*
518  * pts_GroupCreate - create a new group
519  *
520  * PARAMETERS
521  *
522  * IN cellHandle - a previously opened cellHandle that corresponds
523  * to the cell where the group exists.
524  *
525  * IN newGroup - the group to be created.
526  *
527  * IN newOwner - the owner of the group.  Pass NULL if the current user
528  * is to be the new owner, or the character string of the owner otherwise.
529  *
530  * IN/OUT newGroupId - the pts id of the group.  Pass 0 to have ptserver 
531  * generate a value, != 0 to assign a value on your own.  The group id
532  * that is used to create the group is copied into this parameter in the
533  * event you pass in 0.
534  *
535  * LOCKS
536  *
537  * No locks are obtained or released by this function
538  *
539  * RETURN CODES
540  *
541  * Returns != 0 upon successful completion.
542  *
543  */
544
545 int ADMINAPI
546 pts_GroupCreate(const void *cellHandle, const char *newGroup,
547                 const char *newOwner, int *newGroupId, afs_status_p st)
548 {
549     int rc = 0;
550     afs_status_t tst = 0;
551     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
552     afs_int32 newOwnerId = 0;
553
554     /*
555      * Validate arguments
556      */
557
558     if (!IsValidCellHandle(c_handle, &tst)) {
559         goto fail_pts_GroupCreate;
560     }
561
562     if ((newGroup == NULL) || (*newGroup == 0)) {
563         tst = ADMPTSNEWGROUPNULL;
564         goto fail_pts_GroupCreate;
565     }
566
567     if (newGroupId == NULL) {
568         tst = ADMPTSNEWGROUPIDNULL;
569         goto fail_pts_GroupCreate;
570     }
571
572     if (*newGroupId > 0) {
573         tst = ADMPTSNEWGROUPIDPOSITIVE;
574         goto fail_pts_GroupCreate;
575     }
576
577     /*
578      * If a newOwner was specified, validate that it exists
579      */
580
581     if (newOwner != NULL) {
582         if (!TranslateOneName
583             (c_handle, newOwner, ADMPTSNEWOWNERTOOLONG, &newOwnerId, &tst)) {
584             goto fail_pts_GroupCreate;
585         }
586     }
587
588     /*
589      * We make a different rpc based upon the input to this function
590      */
591
592     if (*newGroupId != 0) {
593         tst =
594             ubik_PR_INewEntry(c_handle->pts, 0, newGroup, *newGroupId,
595                       newOwnerId);
596     } else {
597         tst =
598             ubik_PR_NewEntry(c_handle->pts, 0, newGroup, PRGRP,
599                       newOwnerId, newGroupId);
600     }
601
602     if (tst != 0) {
603         goto fail_pts_GroupCreate;
604     }
605     rc = 1;
606
607   fail_pts_GroupCreate:
608
609     if (st != NULL) {
610         *st = tst;
611     }
612     return rc;
613 }
614
615 /*
616  * GetGroupAccess - a small convenience function for setting
617  * permissions.
618  *
619  * PARAMETERS
620  *
621  * IN access - a pointer to a pts_groupAccess_t to be set with the
622  * correct permission.
623  *
624  * IN flag - the current permission flag used to derive the permission.
625  *
626  * LOCKS
627  *
628  * No locks are obtained or released by this function
629  *
630  * RETURN CODES
631  *
632  * Since this function cannot fail, it returns void.
633  *
634  */
635
636 static void
637 GetGroupAccess(pts_groupAccess_p access, afs_int32 flag)
638 {
639
640     *access = PTS_GROUP_OWNER_ACCESS;
641     if (flag == 0) {
642         *access = PTS_GROUP_OWNER_ACCESS;
643     } else if (flag == 1) {
644         *access = PTS_GROUP_ACCESS;
645     } else if (flag == 2) {
646         *access = PTS_GROUP_ANYUSER_ACCESS;
647     }
648 }
649
650
651 /*
652  * pts_GroupGet - retrieve information about a particular group.
653  *
654  * PARAMETERS
655  *
656  * IN cellHandle - a previously opened cellHandle that corresponds
657  * to the cell where the group exists.
658  *
659  * IN groupName - the group to retrieve.
660  *
661  * OUT groupP - a pointer to a pts_GroupEntry_t structure that upon
662  * successful completion is filled with information about groupName.
663  *
664  * LOCKS
665  *
666  * No locks are obtained or released by this function
667  *
668  * RETURN CODES
669  *
670  * Returns != 0 upon successful completion.
671  *
672  */
673
674 int ADMINAPI
675 pts_GroupGet(const void *cellHandle, const char *groupName,
676              pts_GroupEntry_p groupP, afs_status_p st)
677 {
678     int rc = 0;
679     afs_status_t tst = 0;
680     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
681     afs_int32 groupId = 0;
682     afs_int32 flags;
683     afs_int32 twobit;
684     struct prcheckentry groupEntry;
685     idlist ids;
686     afs_int32 ptsids[2];
687     namelist names;
688
689     /*
690      * Validate arguments
691      */
692
693     if (!IsValidCellHandle(c_handle, &tst)) {
694         goto fail_pts_GroupGet;
695     }
696
697     if ((groupName == NULL) || (*groupName == 0)) {
698         tst = ADMPTSGROUPNAMENULL;
699         goto fail_pts_GroupGet;
700     }
701
702     if (groupP == NULL) {
703         tst = ADMPTSGROUPPNULL;
704         goto fail_pts_GroupGet;
705     }
706
707     /*
708      * Translate the group name into an id.
709      */
710
711     if (!TranslateOneName
712         (c_handle, groupName, ADMPTSGROUPNAMETOOLONG, &groupId, &tst)) {
713         goto fail_pts_GroupGet;
714     }
715
716     /*
717      * Retrieve information about the group
718      */
719
720     tst = ubik_PR_ListEntry(c_handle->pts, 0, groupId, &groupEntry);
721
722     if (tst != 0) {
723         goto fail_pts_GroupGet;
724     }
725
726     groupP->membershipCount = groupEntry.count;
727     groupP->nameUid = groupEntry.id;
728     groupP->ownerUid = groupEntry.owner;
729     groupP->creatorUid = groupEntry.creator;
730     strncpy(groupP->name, groupEntry.name, PTS_MAX_NAME_LEN);
731     groupP->name[PTS_MAX_NAME_LEN - 1] = '\0';
732     /*
733      * Set the access rights based upon the value of the flags member
734      * of the groupEntry struct.
735      *
736      * To the best of my ability to decypher the pts code, it looks like
737      * the rights are stored in flags as follows:
738      *
739      * I number my bits from least significant to most significant starting
740      * with 0.
741      *
742      * remove - bit 0
743      *     if bit 0 == 0 -> r access is denied
744      *     if bit 0 == 1 -> r access is granted
745      *
746      * add - bits 1 and 2
747      *     if bit 2 == 0 and bit 1 == 0 -> a access is denied
748      *     if bit 2 == 0 and bit 1 == 1 -> a access is granted
749      *     if bit 2 == 1 and bit 1 == 0 -> A access is granted
750      *     if bit 2 == 1 and bit 1 == 1 -> this is an error
751      *
752      * membership - bits 3 and 4
753      *     if bit 4 == 0 and bit 3 == 0 -> m access is denied
754      *     if bit 4 == 0 and bit 3 == 1 -> m access is granted
755      *     if bit 4 == 1 and bit 3 == 0 -> M access is granted
756      *     if bit 4 == 1 and bit 3 == 1 -> this is an error
757      *
758      * owned - bit 5
759      *     if bit 5 == 0 -> O access is denied
760      *     if bit 5 == 1 -> O access is granted
761      *
762      * status - bits 6 and 7
763      *     if bit 7 == 0 and bit 6 == 0 -> s access is denied
764      *     if bit 7 == 0 and bit 6 == 1 -> s access is granted
765      *     if bit 7 == 1 and bit 6 == 0 -> S access is granted
766      *     if bit 7 == 1 and bit 6 == 1 -> this is an error
767      *
768      * For cases where the permission doesn't make sense for the
769      * type of entry, or where an error occurs, we ignore it.
770      * This is the behavior of the pts code.
771      */
772
773     flags = groupEntry.flags;
774     if (flags & 1) {
775         groupP->listDelete = PTS_GROUP_ACCESS;
776     } else {
777         groupP->listDelete = PTS_GROUP_OWNER_ACCESS;
778     }
779
780     flags = flags >> 1;
781     twobit = flags & 3;
782
783     GetGroupAccess(&groupP->listAdd, twobit);
784
785     flags = flags >> 2;
786     twobit = flags & 3;
787
788     GetGroupAccess(&groupP->listMembership, twobit);
789
790     flags = flags >> 2;
791
792     if (flags & 1) {
793         groupP->listGroupsOwned = PTS_GROUP_ANYUSER_ACCESS;
794     } else {
795         groupP->listGroupsOwned = PTS_GROUP_OWNER_ACCESS;
796     }
797
798     flags = flags >> 1;
799     twobit = flags & 3;
800
801     GetGroupAccess(&groupP->listStatus, twobit);
802
803     /* 
804      * Make another rpc and translate the owner and creator ids into
805      * character strings.
806      */
807
808     ids.idlist_len = 2;
809     ids.idlist_val = ptsids;
810     ptsids[0] = groupEntry.owner;
811     ptsids[1] = groupEntry.creator;
812     names.namelist_len = 0;
813     names.namelist_val = 0;
814
815
816     if (!TranslatePTSIds(c_handle, &names, &ids, &tst)) {
817         goto fail_pts_GroupGet;
818     }
819
820     strncpy(groupP->owner, names.namelist_val[0], PTS_MAX_NAME_LEN);
821     groupP->owner[PTS_MAX_NAME_LEN - 1] = '\0';
822     strncpy(groupP->creator, names.namelist_val[1], PTS_MAX_NAME_LEN);
823     groupP->creator[PTS_MAX_NAME_LEN - 1] = '\0';
824     free(names.namelist_val);
825     rc = 1;
826
827   fail_pts_GroupGet:
828
829     if (st != NULL) {
830         *st = tst;
831     }
832     return rc;
833 }
834
835 /*
836  * EntryDelete - delete a pts entry (group or user).
837  *
838  * PARAMETERS
839  *
840  * IN cellHandle - a previously opened cellHandle that corresponds
841  * to the cell where the group exists.
842  *
843  * IN entryName - the entry to be deleted.
844  *
845  * IN error1 - the error status to be returned in the event that entryName is
846  * null.
847  *
848  * IN error2 - the error status to be returned in the event that entryName is
849  * too long.
850  *
851  * LOCKS
852  *
853  * No locks are obtained or released by this function
854  *
855  * RETURN CODES
856  *
857  * Returns != 0 upon successful completion.
858  *
859  */
860
861 static int
862 EntryDelete(const void *cellHandle, const char *entryName,
863             afs_status_t error1, afs_status_t error2, afs_status_p st)
864 {
865     int rc = 0;
866     afs_status_t tst = 0;
867     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
868     afs_int32 entryId = 0;
869
870     /*
871      * Validate arguments
872      */
873
874     if (!IsValidCellHandle(c_handle, &tst)) {
875         goto fail_EntryDelete;
876     }
877
878     if ((entryName == NULL) || (*entryName == 0)) {
879         tst = error1;
880         goto fail_EntryDelete;
881     }
882
883     /*
884      * Translate the entry name into an id.
885      */
886
887     if (!TranslateOneName(c_handle, entryName, error2, &entryId, &tst)) {
888         goto fail_EntryDelete;
889     }
890
891     /*
892      * Make the rpc
893      */
894
895     tst = ubik_PR_Delete(c_handle->pts, 0, entryId);
896
897     if (tst != 0) {
898         goto fail_EntryDelete;
899     }
900     rc = 1;
901
902   fail_EntryDelete:
903
904     if (st != NULL) {
905         *st = tst;
906     }
907     return rc;
908 }
909
910
911 /*
912  * pts_GroupDelete - delete a group
913  *
914  * PARAMETERS
915  *
916  * IN cellHandle - a previously opened cellHandle that corresponds
917  * to the cell where the group exists.
918  *
919  * IN groupName - the group to be deleted.
920  *
921  * LOCKS
922  *
923  * No locks are obtained or released by this function
924  *
925  * RETURN CODES
926  *
927  * Returns != 0 upon successful completion.
928  *
929  */
930
931 int ADMINAPI
932 pts_GroupDelete(const void *cellHandle, const char *groupName,
933                 afs_status_p st)
934 {
935
936     return EntryDelete(cellHandle, groupName, ADMPTSGROUPNAMENULL,
937                        ADMPTSGROUPNAMETOOLONG, st);
938 }
939
940 /*
941  * pts_GroupMaxGet - get the maximum in use group id.
942  *
943  * PARAMETERS
944  *
945  * IN cellHandle - a previously opened cellHandle that corresponds
946  * to the cell where the group exists.
947  *
948  * OUT maxGroupId - upon successful completion contains the maximum
949  * group Id in use at the server.
950  *
951  * LOCKS
952  *
953  * No locks are obtained or released by this function
954  *
955  * RETURN CODES
956  *
957  * Returns != 0 upon successful completion.
958  *
959  */
960
961 int ADMINAPI
962 pts_GroupMaxGet(const void *cellHandle, int *maxGroupId, afs_status_p st)
963 {
964     int rc = 0;
965     afs_status_t tst = 0;
966     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
967     afs_int32 maxUserId = 0;
968
969     /*
970      * Validate arguments
971      */
972
973     if (!IsValidCellHandle(c_handle, &tst)) {
974         goto fail_pts_GroupMaxGet;
975     }
976
977     if (maxGroupId == NULL) {
978         tst = ADMPTSMAXGROUPIDNULL;
979         goto fail_pts_GroupMaxGet;
980     }
981
982     tst = ubik_PR_ListMax(c_handle->pts, 0, &maxUserId, maxGroupId);
983
984     if (tst != 0) {
985         goto fail_pts_GroupMaxGet;
986     }
987     rc = 1;
988
989   fail_pts_GroupMaxGet:
990
991     if (st != NULL) {
992         *st = tst;
993     }
994     return rc;
995 }
996
997 /*
998  * pts_GroupMaxSet - set the maximum in use group id.
999  *
1000  * PARAMETERS
1001  *
1002  * IN cellHandle - a previously opened cellHandle that corresponds
1003  * to the cell where the group exists.
1004  *
1005  * IN maxGroupId - the new maximum group id.
1006  *
1007  * LOCKS
1008  *
1009  * No locks are obtained or released by this function
1010  *
1011  * RETURN CODES
1012  *
1013  * Returns != 0 upon successful completion.
1014  *
1015  */
1016
1017 int ADMINAPI
1018 pts_GroupMaxSet(const void *cellHandle, int maxGroupId, afs_status_p st)
1019 {
1020     int rc = 0;
1021     afs_status_t tst = 0;
1022     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1023
1024     /*
1025      * Validate arguments
1026      */
1027
1028     if (!IsValidCellHandle(c_handle, &tst)) {
1029         goto fail_pts_GroupMaxSet;
1030     }
1031
1032     tst = ubik_PR_SetMax(c_handle->pts, 0, maxGroupId, PRGRP);
1033
1034     if (tst != 0) {
1035         goto fail_pts_GroupMaxSet;
1036     }
1037     rc = 1;
1038
1039   fail_pts_GroupMaxSet:
1040
1041     if (st != NULL) {
1042         *st = tst;
1043     }
1044     return rc;
1045 }
1046
1047 /*
1048  * NOTE
1049  *
1050  * I'm not using the common iterator pattern here since the retrival
1051  * of the member list is actually accomplished in 1 rpc.  There's no
1052  * sense in trying to fit this pts specific behavior into the more
1053  * generic model, so instead the Begin functions actually do all the
1054  * rpc work and the next/done functions just manipulate the retrieved
1055  * data.
1056  */
1057
1058 typedef struct pts_group_member_list_iterator {
1059     int begin_magic;
1060     int is_valid;
1061     pthread_mutex_t mutex;      /* hold to manipulate this structure */
1062     prlist ids;
1063     namelist names;
1064     int index;
1065     int end_magic;
1066 } pts_group_member_list_iterator_t, *pts_group_member_list_iterator_p;
1067
1068 /*
1069  * pts_GroupMemberListBegin - begin iterating over the list of members
1070  * of a particular group.
1071  *
1072  * PARAMETERS
1073  *
1074  * IN iter - an iterator previously returned by pts_GroupMemberListBegin
1075  *
1076  * LOCKS
1077  *
1078  * No locks are obtained or released by this function
1079  *
1080  * RETURN CODES
1081  *
1082  * Returns != 0 upon successful completion.
1083  *
1084  */
1085
1086 static int
1087 IsValidPtsGroupMemberListIterator(pts_group_member_list_iterator_p iter,
1088                                   afs_status_p st)
1089 {
1090     int rc = 0;
1091     afs_status_t tst = 0;
1092
1093     if (iter == NULL) {
1094         tst = ADMITERATORNULL;
1095         goto fail_IsValidPtsGroupMemberListIterator;
1096     }
1097
1098     if ((iter->begin_magic != BEGIN_MAGIC) || (iter->end_magic != END_MAGIC)) {
1099         tst = ADMITERATORBADMAGICNULL;
1100         goto fail_IsValidPtsGroupMemberListIterator;
1101     }
1102
1103     if (iter->is_valid == 0) {
1104         tst = ADMITERATORINVALID;
1105         goto fail_IsValidPtsGroupMemberListIterator;
1106     }
1107     rc = 1;
1108
1109   fail_IsValidPtsGroupMemberListIterator:
1110
1111     if (st != NULL) {
1112         *st = tst;
1113     }
1114     return rc;
1115 }
1116
1117 /*
1118  * MemberListBegin - an internal function which is used to get both
1119  * the list of members in a group and the list of groups a user belongs
1120  * to.
1121  *
1122  * PARAMETERS
1123  *
1124  * IN cellHandle - a previously opened cellHandle that corresponds
1125  * to the cell where the group exists.
1126  *
1127  * IN name - the name whose membership will be retrieved.
1128  *
1129  * OUT iterationIdP - upon successful completion contains a iterator that
1130  * can be passed to pts_GroupMemberListNext or pts_UserMemberListNext
1131  *
1132  * LOCKS
1133  *
1134  * No locks are obtained or released by this function
1135  *
1136  * RETURN CODES
1137  *
1138  * Returns != 0 upon successful completion.
1139  *
1140  */
1141
1142 static int
1143 MemberListBegin(const void *cellHandle, const char *name, afs_status_t error1,
1144                 afs_status_t error2, void **iterationIdP, afs_status_p st)
1145 {
1146     int rc = 0;
1147     afs_status_t tst = 0;
1148     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1149     afs_int32 groupId = 0;
1150     afs_int32 exceeded = 0;
1151     pts_group_member_list_iterator_p iter = (pts_group_member_list_iterator_p)
1152         malloc(sizeof(pts_group_member_list_iterator_t));
1153     int iter_allocated = 0;
1154     int ids_allocated = 0;
1155     int names_allocated = 0;
1156     int mutex_inited = 0;
1157
1158     /*
1159      * Validate arguments
1160      */
1161
1162     if (!IsValidCellHandle(c_handle, &tst)) {
1163         goto fail_MemberListBegin;
1164     }
1165
1166     if ((name == NULL) || (*name == 0)) {
1167         tst = ADMPTSGROUPNAMENULL;
1168         goto fail_MemberListBegin;
1169     }
1170
1171     if (iterationIdP == NULL) {
1172         tst = ADMITERATORNULL;
1173         goto fail_MemberListBegin;
1174     }
1175
1176     if (iter == NULL) {
1177         tst = ADMNOMEM;
1178         goto fail_MemberListBegin;
1179     }
1180
1181     iter_allocated = 1;
1182
1183     /*
1184      * Translate the name into an id.
1185      */
1186
1187     if (!TranslateOneName
1188         (c_handle, name, ADMPTSGROUPNAMETOOLONG, &groupId, &tst)) {
1189         goto fail_MemberListBegin;
1190     }
1191
1192     if (pthread_mutex_init(&iter->mutex, 0)) {
1193         tst = ADMMUTEXINIT;
1194         goto fail_MemberListBegin;
1195     }
1196
1197     mutex_inited = 1;
1198
1199     iter->ids.prlist_len = 0;
1200     iter->ids.prlist_val = 0;
1201
1202     tst =
1203         ubik_PR_ListElements(c_handle->pts, 0, groupId, &iter->ids,
1204                   &exceeded);
1205
1206     if (tst != 0) {
1207         goto fail_MemberListBegin;
1208     }
1209
1210     if (exceeded != 0) {
1211         tst = ADMPTSGROUPMEMEXCEEDED;
1212         goto fail_MemberListBegin;
1213     }
1214
1215     ids_allocated = 1;
1216     iter->names.namelist_len = 0;
1217     iter->names.namelist_val = 0;
1218
1219     if (!TranslatePTSIds
1220         (c_handle, &iter->names, (idlist *) & iter->ids, &tst)) {
1221         goto fail_MemberListBegin;
1222     }
1223
1224     names_allocated = 1;
1225     iter->begin_magic = BEGIN_MAGIC;
1226     iter->end_magic = END_MAGIC;
1227     iter->index = 0;
1228     iter->is_valid = 1;
1229
1230     *iterationIdP = (void *)iter;
1231     rc = 1;
1232
1233   fail_MemberListBegin:
1234
1235     if (ids_allocated) {
1236         free(iter->ids.prlist_val);
1237     }
1238
1239     if (rc == 0) {
1240         if (names_allocated) {
1241             free(iter->names.namelist_val);
1242         }
1243         if (mutex_inited) {
1244             pthread_mutex_destroy(&iter->mutex);
1245         }
1246         if (iter_allocated) {
1247             free(iter);
1248         }
1249     }
1250     if (st != NULL) {
1251         *st = tst;
1252     }
1253     return rc;
1254 }
1255
1256
1257 /*
1258  * pts_GroupMemberListBegin - begin iterating over the list of members
1259  * of a particular group.
1260  *
1261  * PARAMETERS
1262  *
1263  * IN cellHandle - a previously opened cellHandle that corresponds
1264  * to the cell where the group exists.
1265  *
1266  * IN groupName - the group whose members will be returned.
1267  *
1268  * OUT iterationIdP - upon successful completion contains a iterator that
1269  * can be passed to pts_GroupMemberListNext.
1270  *
1271  * LOCKS
1272  *
1273  * No locks are obtained or released by this function
1274  *
1275  * RETURN CODES
1276  *
1277  * Returns != 0 upon successful completion.
1278  *
1279  */
1280
1281 int ADMINAPI
1282 pts_GroupMemberListBegin(const void *cellHandle, const char *groupName,
1283                          void **iterationIdP, afs_status_p st)
1284 {
1285     return MemberListBegin(cellHandle, groupName, ADMPTSGROUPNAMENULL,
1286                            ADMPTSGROUPNAMETOOLONG, iterationIdP, st);
1287 }
1288
1289 /*
1290  * pts_GroupMemberListNext - get the next member of a group
1291  *
1292  * PARAMETERS
1293  *
1294  * IN iterationId - an iterator previously returned by pts_GroupMemberListBegin
1295  *
1296  * OUT memberName - upon successful completion contains the next member of
1297  * a group.
1298  *
1299  * LOCKS
1300  *
1301  * The iterator mutex is held during the retrieval of the next member.
1302  *
1303  * RETURN CODES
1304  *
1305  * Returns != 0 upon successful completion.
1306  *
1307  */
1308
1309 int ADMINAPI
1310 pts_GroupMemberListNext(const void *iterationId, char *memberName,
1311                         afs_status_p st)
1312 {
1313     int rc = 0;
1314     afs_status_t tst = 0;
1315     pts_group_member_list_iterator_p iter =
1316         (pts_group_member_list_iterator_p) iterationId;
1317     int mutex_locked = 0;
1318
1319     /*
1320      * Validate arguments
1321      */
1322
1323     if (iter == NULL) {
1324         tst = ADMITERATORNULL;
1325         goto fail_pts_GroupMemberListNext;
1326     }
1327
1328     if (memberName == NULL) {
1329         tst = ADMPTSMEMBERNAMENULL;
1330         goto fail_pts_GroupMemberListNext;
1331     }
1332
1333     /*
1334      * Lock the mutex and check the validity of the iterator
1335      */
1336
1337     if (pthread_mutex_lock(&iter->mutex)) {
1338         tst = ADMMUTEXLOCK;
1339         goto fail_pts_GroupMemberListNext;
1340     }
1341
1342     mutex_locked = 1;
1343
1344     if (!IsValidPtsGroupMemberListIterator(iter, &tst)) {
1345         goto fail_pts_GroupMemberListNext;
1346     }
1347
1348     /*
1349      * Check to see if we've copied out all the data.  If we haven't,
1350      * copy another item.  If we have, mark the iterator done.
1351      */
1352
1353     if (iter->index >= iter->names.namelist_len) {
1354         tst = ADMITERATORDONE;
1355         goto fail_pts_GroupMemberListNext;
1356     } else {
1357         strcpy(memberName, iter->names.namelist_val[iter->index]);
1358         iter->index++;
1359     }
1360     rc = 1;
1361
1362   fail_pts_GroupMemberListNext:
1363
1364     if (mutex_locked) {
1365         pthread_mutex_unlock(&iter->mutex);
1366     }
1367
1368     if (st != NULL) {
1369         *st = tst;
1370     }
1371     return rc;
1372 }
1373
1374 /*
1375  * pts_GroupMemberListDone - finish using a member list iterator
1376  *
1377  * PARAMETERS
1378  *
1379  * IN iterationId - an iterator previously returned by pts_GroupMemberListBegin
1380  *
1381  * LOCKS
1382  *
1383  * The iterator is locked and then destroyed
1384  *
1385  * RETURN CODES
1386  *
1387  * Returns != 0 upon successful completion.
1388  *
1389  * ASSUMPTIONS
1390  *
1391  * It is the user's responsibility to make sure pts_GroupMemberListDone
1392  * is called only once for each iterator.
1393  */
1394
1395 int ADMINAPI
1396 pts_GroupMemberListDone(const void *iterationId, afs_status_p st)
1397 {
1398     int rc = 0;
1399     afs_status_t tst = 0;
1400     pts_group_member_list_iterator_p iter =
1401         (pts_group_member_list_iterator_p) iterationId;
1402     int mutex_locked = 0;
1403
1404     /*
1405      * Validate arguments
1406      */
1407
1408     if (iter == NULL) {
1409         tst = ADMITERATORNULL;
1410         goto fail_pts_GroupMemberListDone;
1411     }
1412
1413     /*
1414      * Lock the mutex and check the validity of the iterator
1415      */
1416
1417     if (pthread_mutex_lock(&iter->mutex)) {
1418         tst = ADMMUTEXLOCK;
1419         goto fail_pts_GroupMemberListDone;
1420     }
1421
1422     mutex_locked = 1;
1423
1424     if (!IsValidPtsGroupMemberListIterator(iter, &tst)) {
1425         goto fail_pts_GroupMemberListDone;
1426     }
1427
1428     /*
1429      * Free the namelist and the iterator.
1430      */
1431
1432     pthread_mutex_destroy(&iter->mutex);
1433     mutex_locked = 0;
1434     iter->is_valid = 0;
1435     free(iter->names.namelist_val);
1436     free(iter);
1437     rc = 1;
1438
1439   fail_pts_GroupMemberListDone:
1440
1441     if (mutex_locked) {
1442         pthread_mutex_unlock(&iter->mutex);
1443     }
1444
1445     if (st != NULL) {
1446         *st = tst;
1447     }
1448     return rc;
1449 }
1450
1451 /*
1452  * pts_GroupMemberRemove - remove a member from a group.
1453  *
1454  * PARAMETERS
1455  *
1456  * IN cellHandle - a previously opened cellHandle that corresponds
1457  * to the cell where the group exists.
1458  *
1459  * IN userName - the user to remove.
1460  *
1461  * IN groupName - the group to modify
1462  *
1463  * LOCKS
1464  *
1465  * No locks are held by this function
1466  *
1467  * RETURN CODES
1468  *
1469  * Returns != 0 upon successful completion.
1470  *
1471  */
1472
1473 int ADMINAPI
1474 pts_GroupMemberRemove(const void *cellHandle, const char *userName,
1475                       const char *groupName, afs_status_p st)
1476 {
1477     int rc = 0;
1478     afs_status_t tst = 0;
1479     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1480     idlist ids;
1481
1482     /*
1483      * Validate arguments
1484      */
1485
1486     if (!IsValidCellHandle(c_handle, &tst)) {
1487         goto fail_pts_GroupMemberRemove;
1488     }
1489
1490     if ((userName == NULL) || (*userName == 0)) {
1491         tst = ADMPTSUSERNAMENULL;
1492         goto fail_pts_GroupMemberRemove;
1493     }
1494
1495     if ((groupName == NULL) || (*groupName == 0)) {
1496         tst = ADMPTSGROUPNAMENULL;
1497         goto fail_pts_GroupMemberRemove;
1498     }
1499
1500     if (!TranslateTwoNames
1501         (c_handle, userName, ADMPTSUSERNAMETOOLONG, groupName,
1502          ADMPTSGROUPNAMETOOLONG, &ids, &tst)) {
1503         goto fail_pts_GroupMemberRemove;
1504     }
1505
1506     /*
1507      * Make the rpc
1508      */
1509
1510     tst =
1511         ubik_PR_RemoveFromGroup(c_handle->pts, 0, ids.idlist_val[0],
1512                   ids.idlist_val[1]);
1513
1514     if (tst != 0) {
1515         goto fail_pts_GroupMemberRemove;
1516     }
1517     rc = 1;
1518
1519   fail_pts_GroupMemberRemove:
1520
1521     if (ids.idlist_val != 0) {
1522         free(ids.idlist_val);
1523     }
1524
1525     if (st != NULL) {
1526         *st = tst;
1527     }
1528     return rc;
1529 }
1530
1531 /*
1532  * pts_GroupRename - change the name of a group
1533  *
1534  * PARAMETERS
1535  *
1536  * IN cellHandle - a previously opened cellHandle that corresponds
1537  * to the cell where the group exists.
1538  *
1539  * IN oldName - the current group name
1540  *
1541  * IN newName - the new group name
1542  *
1543  * LOCKS
1544  *
1545  * No locks are held by this function
1546  *
1547  * RETURN CODES
1548  *
1549  * Returns != 0 upon successful completion.
1550  *
1551  */
1552
1553 int ADMINAPI
1554 pts_GroupRename(const void *cellHandle, const char *oldName,
1555                 const char *newName, afs_status_p st)
1556 {
1557     int rc = 0;
1558     afs_status_t tst = 0;
1559     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1560     afs_int32 groupId = 0;
1561
1562     /*
1563      * Validate arguments
1564      */
1565
1566     if (!IsValidCellHandle(c_handle, &tst)) {
1567         goto fail_pts_GroupRename;
1568     }
1569
1570     if ((newName == NULL) || (*newName == 0)) {
1571         tst = ADMPTSNEWNAMENULL;
1572         goto fail_pts_GroupRename;
1573     }
1574
1575     if ((oldName == NULL) || (*oldName == 0)) {
1576         tst = ADMPTSOLDNAMENULL;
1577         goto fail_pts_GroupRename;
1578     }
1579
1580     /*
1581      * Translate the group name into an id.
1582      */
1583
1584     if (!TranslateOneName
1585         (c_handle, oldName, ADMPTSOLDNAMETOOLONG, &groupId, &tst)) {
1586         goto fail_pts_GroupRename;
1587     }
1588
1589     /*
1590      * Make the rpc
1591      */
1592
1593     tst = ubik_PR_ChangeEntry(c_handle->pts, 0, groupId, newName, 0, 0);
1594
1595     if (tst != 0) {
1596         goto fail_pts_GroupRename;
1597     }
1598     rc = 1;
1599
1600   fail_pts_GroupRename:
1601
1602     if (st != NULL) {
1603         *st = tst;
1604     }
1605     return rc;
1606 }
1607
1608 /*
1609  * SetGroupAccess - translate our Access notation to pts flags.
1610  *
1611  * PARAMETERS
1612  *
1613  * IN rights - the permissions.
1614  *
1615  * OUT flags - a pointer to an afs_int32 structure that 
1616  * contains the flags to pass to pts.
1617  *
1618  * LOCKS
1619  *
1620  * No locks are held by this function
1621  *
1622  * RETURN CODES
1623  *
1624  * Returns != 0 upon successful completion.
1625  *
1626  */
1627
1628 static int
1629 SetGroupAccess(const pts_GroupUpdateEntry_p rights, afs_int32 * flags,
1630                afs_status_p st)
1631 {
1632     int rc = 0;
1633     afs_status_t tst = 0;
1634
1635     *flags = 0;
1636
1637     if (rights->listDelete == PTS_GROUP_ACCESS) {
1638         *flags |= 1;
1639     } else if (rights->listDelete == PTS_GROUP_ANYUSER_ACCESS) {
1640         tst = ADMPTSINVALIDGROUPDELETEPERM;
1641         goto fail_SetGroupAccess;
1642     }
1643
1644     if (rights->listAdd == PTS_GROUP_ACCESS) {
1645         *flags |= 2;
1646     } else if (rights->listAdd == PTS_GROUP_ANYUSER_ACCESS) {
1647         *flags |= 4;
1648     }
1649
1650     if (rights->listMembership == PTS_GROUP_ACCESS) {
1651         *flags |= 8;
1652     } else if (rights->listMembership == PTS_GROUP_ANYUSER_ACCESS) {
1653         *flags |= 16;
1654     }
1655
1656     if (rights->listGroupsOwned == PTS_GROUP_ANYUSER_ACCESS) {
1657         *flags |= 32;
1658     } else if (rights->listGroupsOwned == PTS_GROUP_ACCESS) {
1659         tst = ADMPTSINVALIDGROUPSOWNEDPERM;
1660         goto fail_SetGroupAccess;
1661     }
1662
1663     if (rights->listStatus == PTS_GROUP_ACCESS) {
1664         *flags |= 64;
1665     } else if (rights->listStatus == PTS_GROUP_ANYUSER_ACCESS) {
1666         *flags |= 128;
1667     }
1668     rc = 1;
1669
1670   fail_SetGroupAccess:
1671
1672     if (st != NULL) {
1673         *st = tst;
1674     }
1675     return rc;
1676 }
1677
1678 /*
1679  * pts_GroupModify - change the contents of a group entry.
1680  *
1681  * PARAMETERS
1682  *
1683  * IN cellHandle - a previously opened cellHandle that corresponds
1684  * to the cell where the group exists.
1685  *
1686  * IN groupName - the group to change
1687  *
1688  * OUT newEntryP - a pointer to a pts_GroupUpdateEntry_t structure that 
1689  * contains the new information for the group.
1690  *
1691  * LOCKS
1692  *
1693  * No locks are held by this function
1694  *
1695  * RETURN CODES
1696  *
1697  * Returns != 0 upon successful completion.
1698  *
1699  */
1700
1701 int ADMINAPI
1702 pts_GroupModify(const void *cellHandle, const char *groupName,
1703                 const pts_GroupUpdateEntry_p newEntryP, afs_status_p st)
1704 {
1705     int rc = 0;
1706     afs_status_t tst = 0;
1707     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1708     afs_int32 groupId = 0;
1709     afs_int32 flags = 0;
1710
1711     /*
1712      * Validate arguments
1713      */
1714
1715     if (!IsValidCellHandle(c_handle, &tst)) {
1716         goto fail_pts_GroupModify;
1717     }
1718
1719     if ((groupName == NULL) || (*groupName == 0)) {
1720         tst = ADMPTSGROUPNAMENULL;
1721         goto fail_pts_GroupModify;
1722     }
1723
1724
1725     if (newEntryP == NULL) {
1726         tst = ADMPTSNEWENTRYPNULL;
1727         goto fail_pts_GroupModify;
1728     }
1729
1730     /*
1731      * Translate the group name into an id.
1732      */
1733
1734     if (!TranslateOneName
1735         (c_handle, groupName, ADMPTSGROUPNAMETOOLONG, &groupId, &tst)) {
1736         goto fail_pts_GroupModify;
1737     }
1738
1739     /*
1740      * Set the flags argument
1741      */
1742
1743     if (!SetGroupAccess(newEntryP, &flags, &tst)) {
1744         goto fail_pts_GroupModify;
1745     }
1746
1747     /*
1748      * Make the rpc
1749      */
1750
1751     tst =
1752         ubik_PR_SetFieldsEntry(c_handle->pts, 0, groupId, PR_SF_ALLBITS,
1753                   flags, 0, 0, 0, 0);
1754
1755     if (tst != 0) {
1756         goto fail_pts_GroupModify;
1757     }
1758     rc = 1;
1759
1760   fail_pts_GroupModify:
1761
1762     if (st != NULL) {
1763         *st = tst;
1764     }
1765     return rc;
1766 }
1767
1768 /*
1769  * pts_UserCreate - create a new user.
1770  *
1771  * PARAMETERS
1772  *
1773  * IN cellHandle - a previously opened cellHandle that corresponds
1774  * to the cell where the group exists.
1775  *
1776  * IN newUser - the name of the new user.
1777  *
1778  * IN newUserId - the id to assign to the new user.  Pass 0 to have the
1779  * id assigned by pts.
1780  *
1781  * LOCKS
1782  *
1783  * No locks are held by this function
1784  *
1785  * RETURN CODES
1786  *
1787  * Returns != 0 upon successful completion.
1788  *
1789  */
1790
1791 int ADMINAPI
1792 pts_UserCreate(const void *cellHandle, const char *userName, int *newUserId,
1793                afs_status_p st)
1794 {
1795     int rc = 0;
1796     afs_status_t tst = 0;
1797     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1798     afs_int32 userId = 0;
1799
1800     /*
1801      * Validate arguments
1802      */
1803
1804     if (!IsValidCellHandle(c_handle, &tst)) {
1805         goto fail_pts_UserCreate;
1806     }
1807
1808     if ((userName == NULL) || (*userName == 0)) {
1809         tst = ADMPTSUSERNAMENULL;
1810         goto fail_pts_UserCreate;
1811     }
1812
1813     if (newUserId == NULL) {
1814         tst = ADMPTSNEWUSERIDNULL;
1815         goto fail_pts_UserCreate;
1816     }
1817
1818     /*
1819      * We make a different rpc based upon the input to this function
1820      */
1821
1822     if (*newUserId != 0) {
1823         tst =
1824             ubik_PR_INewEntry(c_handle->pts, 0, userName, *newUserId,
1825                       0);
1826     } else {
1827         tst =
1828             ubik_PR_NewEntry(c_handle->pts, 0, userName, 0, 0,
1829                       newUserId);
1830     }
1831
1832     if (tst != 0) {
1833         goto fail_pts_UserCreate;
1834     }
1835     rc = 1;
1836
1837   fail_pts_UserCreate:
1838
1839     if (st != NULL) {
1840         *st = tst;
1841     }
1842     return rc;
1843 }
1844
1845 /*
1846  * pts_UserDelete - delete a user.
1847  *
1848  * PARAMETERS
1849  *
1850  * IN cellHandle - a previously opened cellHandle that corresponds
1851  * to the cell where the group exists.
1852  *
1853  * IN user - the name of the user to delete.
1854  *
1855  * LOCKS
1856  *
1857  * No locks are held by this function
1858  *
1859  * RETURN CODES
1860  *
1861  * Returns != 0 upon successful completion.
1862  *
1863  */
1864
1865 int ADMINAPI
1866 pts_UserDelete(const void *cellHandle, const char *userName, afs_status_p st)
1867 {
1868     return EntryDelete(cellHandle, userName, ADMPTSUSERNAMENULL,
1869                        ADMPTSUSERNAMETOOLONG, st);
1870 }
1871
1872
1873 /*
1874  * GetUserAccess - a small convenience function for setting
1875  * permissions.
1876  *
1877  * PARAMETERS
1878  *
1879  * IN access - a pointer to a pts_userAccess_t to be set with the
1880  * correct permission.
1881  *
1882  * IN flag - the current permission flag used to derive the permission.
1883  *
1884  * LOCKS
1885  *
1886  * No locks are obtained or released by this function
1887  *
1888  * RETURN CODES
1889  *
1890  * Since this function cannot fail, it returns void.
1891  *
1892  */
1893
1894 static void
1895 GetUserAccess(pts_userAccess_p access, afs_int32 flag)
1896 {
1897
1898     *access = PTS_USER_OWNER_ACCESS;
1899     if (flag == 2) {
1900         *access = PTS_USER_ANYUSER_ACCESS;
1901     }
1902 }
1903
1904 /*
1905  * IsAdministrator - determine if a user is an administrator.
1906  *
1907  * PARAMETERS
1908  *
1909  * IN cellHandle - a previously opened cellHandle that corresponds
1910  * to the cell where the group exists.
1911  *
1912  * IN userEntry - the user data for the user in question.
1913  *
1914  * OUT admin - set to 1 if the user is an administrator, 0 otherwise.
1915  *
1916  * LOCKS
1917  *
1918  * No locks are held by this function
1919  *
1920  * RETURN CODES
1921  *
1922  * Returns != 0 upon successful completion.
1923  *
1924  */
1925
1926 static int
1927 IsAdministrator(const afs_cell_handle_p c_handle, afs_int32 userId,
1928                 int *admin, afs_status_p st)
1929 {
1930     int rc = 0;
1931     afs_status_t tst = 0;
1932     afs_int32 adminId = 0;
1933     afs_int32 isAdmin = 0;
1934
1935     *admin = 0;
1936
1937     if (userId == SYSADMINID) {
1938         *admin = 1;
1939     } else {
1940         if (!TranslateOneName
1941             (c_handle, "system:administrators", ADMPTSGROUPNAMETOOLONG,
1942              &adminId, &tst)) {
1943             goto fail_IsAdministrator;
1944         }
1945         tst =
1946             ubik_PR_IsAMemberOf(c_handle->pts, 0, userId, adminId,
1947                       &isAdmin);
1948         if (tst != 0) {
1949             goto fail_IsAdministrator;
1950         }
1951         if (isAdmin) {
1952             *admin = 1;
1953         }
1954     }
1955     rc = 1;
1956
1957   fail_IsAdministrator:
1958
1959     if (st != NULL) {
1960         *st = tst;
1961     }
1962     return rc;
1963 }
1964
1965 /*
1966  * pts_UserGet - retrieve information about a particular user.
1967  *
1968  * PARAMETERS
1969  *
1970  * IN cellHandle - a previously opened cellHandle that corresponds
1971  * to the cell where the group exists.
1972  *
1973  * IN userName - the name of the user to retrieve.
1974  *
1975  * OUT userP - a pointer to a pts_UserEntry_t that is filled upon successful
1976  * completion.
1977  *
1978  * LOCKS
1979  *
1980  * No locks are held by this function
1981  *
1982  * RETURN CODES
1983  *
1984  * Returns != 0 upon successful completion.
1985  *
1986  */
1987
1988 int ADMINAPI
1989 pts_UserGet(const void *cellHandle, const char *userName,
1990             pts_UserEntry_p userP, afs_status_p st)
1991 {
1992     int rc = 0;
1993     afs_status_t tst = 0;
1994     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1995     struct prcheckentry userEntry;
1996     afs_int32 userId = 0;
1997     afs_int32 flags;
1998     afs_int32 twobit;
1999     idlist ids;
2000     afs_int32 ptsids[2];
2001     namelist names;
2002     int admin = 0;
2003
2004
2005     /*
2006      * Validate arguments
2007      */
2008
2009     if (!IsValidCellHandle(c_handle, &tst)) {
2010         goto fail_pts_UserGet;
2011     }
2012
2013     if ((userName == NULL) || (*userName == 0)) {
2014         tst = ADMPTSUSERNAMENULL;
2015         goto fail_pts_UserGet;
2016     }
2017
2018     if (userP == NULL) {
2019         tst = ADMPTSUSERPNULL;
2020         goto fail_pts_UserGet;
2021     }
2022
2023     /*
2024      * Translate the group name into an id.
2025      */
2026
2027     if (!TranslateOneName
2028         (c_handle, userName, ADMPTSUSERNAMETOOLONG, &userId, &tst)) {
2029         goto fail_pts_UserGet;
2030     }
2031
2032     /*
2033      * Retrieve information about the group
2034      */
2035
2036     tst = ubik_PR_ListEntry(c_handle->pts, 0, userId, &userEntry);
2037
2038     if (tst != 0) {
2039         goto fail_pts_UserGet;
2040     }
2041
2042     userP->groupMembershipCount = userEntry.count;
2043     userP->groupCreationQuota = userEntry.ngroups;
2044     /*
2045      * The administrator id, or any member of "system:administrators"
2046      * has unlimited group creation quota.  Denote this by setting
2047      * quota to -1.
2048      */
2049
2050     if (!IsAdministrator(c_handle, userEntry.id, &admin, &tst)) {
2051         goto fail_pts_UserGet;
2052     }
2053
2054     if (admin != 0) {
2055         userP->groupCreationQuota = -1;
2056     }
2057
2058     userP->nameUid = userEntry.id;
2059     userP->ownerUid = userEntry.owner;
2060     userP->creatorUid = userEntry.creator;
2061     strncpy(userP->name, userEntry.name, PTS_MAX_NAME_LEN);
2062     userP->name[PTS_MAX_NAME_LEN - 1] = '\0';
2063
2064     /*
2065      * The permission bits are described in the GroupGet function above.
2066      * The user entry only uses 3 of the 5 permissions, so we shift
2067      * past the unused entries.
2068      */
2069
2070     flags = userEntry.flags;
2071     flags = flags >> 3;
2072     twobit = flags & 3;
2073
2074     GetUserAccess(&userP->listMembership, twobit);
2075
2076     flags = flags >> 2;
2077
2078     if (flags & 1) {
2079         userP->listGroupsOwned = PTS_USER_ANYUSER_ACCESS;
2080     } else {
2081         userP->listGroupsOwned = PTS_USER_OWNER_ACCESS;
2082     }
2083
2084     flags = flags >> 1;
2085     twobit = flags & 3;
2086
2087     GetUserAccess(&userP->listStatus, twobit);
2088
2089     /* 
2090      * Make another rpc and translate the owner and creator ids into
2091      * character strings.
2092      */
2093
2094     ids.idlist_len = 2;
2095     ids.idlist_val = ptsids;
2096     ptsids[0] = userEntry.owner;
2097     ptsids[1] = userEntry.creator;
2098     names.namelist_len = 0;
2099     names.namelist_val = 0;
2100
2101
2102     if (!TranslatePTSIds(c_handle, &names, &ids, &tst)) {
2103         goto fail_pts_UserGet;
2104     }
2105
2106     strncpy(userP->owner, names.namelist_val[0], PTS_MAX_NAME_LEN);
2107     userP->owner[PTS_MAX_NAME_LEN - 1] ='\0';
2108     strncpy(userP->creator, names.namelist_val[1], PTS_MAX_NAME_LEN);
2109     userP->creator[PTS_MAX_NAME_LEN - 1] = '\0';
2110     free(names.namelist_val);
2111     rc = 1;
2112
2113   fail_pts_UserGet:
2114
2115     if (st != NULL) {
2116         *st = tst;
2117     }
2118     return rc;
2119 }
2120
2121 /*
2122  * pts_UserRename - rename a user.
2123  *
2124  * PARAMETERS
2125  *
2126  * IN cellHandle - a previously opened cellHandle that corresponds
2127  * to the cell where the group exists.
2128  *
2129  * IN oldName - the name of the user to rename.
2130  *
2131  * IN newName - the new user name.
2132  *
2133  * LOCKS
2134  *
2135  * No locks are held by this function
2136  *
2137  * RETURN CODES
2138  *
2139  * Returns != 0 upon successful completion.
2140  *
2141  */
2142
2143 int ADMINAPI
2144 pts_UserRename(const void *cellHandle, const char *oldName,
2145                const char *newName, afs_status_p st)
2146 {
2147     int rc = 0;
2148     afs_status_t tst = 0;
2149     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2150     afs_int32 userId = 0;
2151
2152     /*
2153      * Validate arguments
2154      */
2155
2156     if (!IsValidCellHandle(c_handle, &tst)) {
2157         goto fail_pts_UserRename;
2158     }
2159
2160     if ((oldName == NULL) || (*oldName == 0)) {
2161         tst = ADMPTSOLDNAMENULL;
2162         goto fail_pts_UserRename;
2163     }
2164
2165     if ((newName == NULL) || (*newName == 0)) {
2166         tst = ADMPTSNEWNAMENULL;
2167         goto fail_pts_UserRename;
2168     }
2169
2170     /*
2171      * Translate the user name into an id.
2172      */
2173
2174     if (!TranslateOneName
2175         (c_handle, oldName, ADMPTSOLDNAMETOOLONG, &userId, &tst)) {
2176         goto fail_pts_UserRename;
2177     }
2178
2179     /*
2180      * Make the rpc
2181      */
2182
2183     tst = ubik_PR_ChangeEntry(c_handle->pts, 0, userId, newName, 0, 0);
2184
2185     if (tst != 0) {
2186         goto fail_pts_UserRename;
2187     }
2188     rc = 1;
2189
2190   fail_pts_UserRename:
2191
2192     if (st != NULL) {
2193         *st = tst;
2194     }
2195     return rc;
2196 }
2197
2198 /*
2199  * SetUserAccess - translate our Access notation to pts flags.
2200  *
2201  * PARAMETERS
2202  *
2203  * IN userP - the user structure that contains the new permissions.
2204  *
2205  * OUT flags - a pointer to an afs_int32 structure that 
2206  * contains the flags to pass to pts.
2207  *
2208  * LOCKS
2209  *
2210  * No locks are held by this function
2211  *
2212  * RETURN CODES
2213  *
2214  * Returns != 0 upon successful completion.
2215  *
2216  */
2217
2218 static int
2219 SetUserAccess(const pts_UserUpdateEntry_p userP, afs_int32 * flags,
2220               afs_status_p st)
2221 {
2222     int rc = 0;
2223     afs_status_t tst = 0;
2224
2225     *flags = 0;
2226
2227     if (userP->listMembership == PTS_USER_ANYUSER_ACCESS) {
2228         *flags |= 16;
2229     }
2230
2231     if (userP->listGroupsOwned == PTS_USER_ANYUSER_ACCESS) {
2232         *flags |= 32;
2233     }
2234
2235     if (userP->listStatus == PTS_USER_ANYUSER_ACCESS) {
2236         *flags |= 128;
2237     }
2238     rc = 1;
2239
2240     if (st != NULL) {
2241         *st = tst;
2242     }
2243     return rc;
2244 }
2245
2246
2247 /*
2248  * pts_UserModify - update a user entry.
2249  *
2250  * PARAMETERS
2251  *
2252  * IN cellHandle - a previously opened cellHandle that corresponds
2253  * to the cell where the group exists.
2254  *
2255  * IN userName - the name of the user to update.
2256  *
2257  * IN newEntryP - a pointer to a pts_UserUpdateEntry_t that contains the
2258  * new information for user.
2259  *
2260  * LOCKS
2261  *
2262  * No locks are held by this function
2263  *
2264  * RETURN CODES
2265  *
2266  * Returns != 0 upon successful completion.
2267  *
2268  */
2269
2270 int ADMINAPI
2271 pts_UserModify(const void *cellHandle, const char *userName,
2272                const pts_UserUpdateEntry_p newEntryP, afs_status_p st)
2273 {
2274     int rc = 0;
2275     afs_status_t tst = 0;
2276     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2277     afs_int32 userId = 0;
2278     afs_int32 newQuota = 0;
2279     afs_int32 mask = 0;
2280     afs_int32 flags = 0;
2281
2282     /*
2283      * Validate arguments
2284      */
2285
2286     if (!IsValidCellHandle(c_handle, &tst)) {
2287         goto fail_pts_UserModify;
2288     }
2289
2290     if ((userName == NULL) || (*userName == 0)) {
2291         tst = ADMPTSUSERNAMENULL;
2292         goto fail_pts_UserModify;
2293     }
2294
2295     if (newEntryP == NULL) {
2296         tst = ADMPTSNEWENTRYPNULL;
2297         goto fail_pts_UserModify;
2298     }
2299
2300     /*
2301      * Translate the user name into an id.
2302      */
2303
2304     if (!TranslateOneName
2305         (c_handle, userName, ADMPTSUSERNAMETOOLONG, &userId, &tst)) {
2306         goto fail_pts_UserModify;
2307     }
2308
2309
2310     if (newEntryP->flag & PTS_USER_UPDATE_GROUP_CREATE_QUOTA) {
2311         mask |= PR_SF_NGROUPS;
2312         newQuota = newEntryP->groupCreationQuota;
2313     }
2314
2315     if (newEntryP->flag & PTS_USER_UPDATE_PERMISSIONS) {
2316         mask |= PR_SF_ALLBITS;
2317         if (!SetUserAccess(newEntryP, &flags, &tst)) {
2318             goto fail_pts_UserModify;
2319         }
2320     }
2321
2322     /*
2323      * Make the rpc
2324      */
2325
2326     tst =
2327         ubik_PR_SetFieldsEntry(c_handle->pts, 0, userId, mask, flags,
2328                   newQuota, 0, 0, 0);
2329
2330     if (tst != 0) {
2331         goto fail_pts_UserModify;
2332     }
2333     rc = 1;
2334
2335   fail_pts_UserModify:
2336
2337     if (st != NULL) {
2338         *st = tst;
2339     }
2340     return rc;
2341 }
2342
2343 /*
2344  * pts_UserMaxGet - get the maximum in use user id.
2345  *
2346  * PARAMETERS
2347  *
2348  * IN cellHandle - a previously opened cellHandle that corresponds
2349  * to the cell where the group exists.
2350  *
2351  * OUT maxUserId - upon successful completion contains the max in use id.
2352  *
2353  * LOCKS
2354  *
2355  * No locks are held by this function
2356  *
2357  * RETURN CODES
2358  *
2359  * Returns != 0 upon successful completion.
2360  *
2361  */
2362
2363 int ADMINAPI
2364 pts_UserMaxGet(const void *cellHandle, int *maxUserId, afs_status_p st)
2365 {
2366     int rc = 0;
2367     afs_status_t tst = 0;
2368     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2369     afs_int32 maxGroupId = 0;
2370
2371     /*
2372      * Validate arguments
2373      */
2374
2375     if (!IsValidCellHandle(c_handle, &tst)) {
2376         goto fail_pts_UserMaxGet;
2377     }
2378
2379     if (maxUserId == NULL) {
2380         tst = ADMPTSMAXUSERIDNULL;
2381         goto fail_pts_UserMaxGet;
2382     }
2383
2384     tst = ubik_PR_ListMax(c_handle->pts, 0, maxUserId, &maxGroupId);
2385
2386     if (tst != 0) {
2387         goto fail_pts_UserMaxGet;
2388     }
2389     rc = 1;
2390
2391   fail_pts_UserMaxGet:
2392
2393     if (st != NULL) {
2394         *st = tst;
2395     }
2396     return rc;
2397 }
2398
2399 /*
2400  * pts_UserMaxSet - set the maximum user id.
2401  *
2402  * PARAMETERS
2403  *
2404  * IN cellHandle - a previously opened cellHandle that corresponds
2405  * to the cell where the group exists.
2406  *
2407  * IN maxUserId - the new max user id.
2408  *
2409  * LOCKS
2410  *
2411  * No locks are held by this function
2412  *
2413  * RETURN CODES
2414  *
2415  * Returns != 0 upon successful completion.
2416  *
2417  */
2418
2419 int ADMINAPI
2420 pts_UserMaxSet(const void *cellHandle, int maxUserId, afs_status_p st)
2421 {
2422     int rc = 0;
2423     afs_status_t tst = 0;
2424     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2425
2426     /*
2427      * Validate arguments
2428      */
2429
2430     if (!IsValidCellHandle(c_handle, &tst)) {
2431         goto fail_pts_UserMaxSet;
2432     }
2433
2434     tst = ubik_PR_SetMax(c_handle->pts, 0, maxUserId, 0);
2435
2436     if (tst != 0) {
2437         goto fail_pts_UserMaxSet;
2438     }
2439     rc = 1;
2440
2441   fail_pts_UserMaxSet:
2442
2443     if (st != NULL) {
2444         *st = tst;
2445     }
2446     return rc;
2447 }
2448
2449 /*
2450  * pts_UserMemberListBegin - begin iterating over the list of groups
2451  * a particular user belongs to.
2452  *
2453  * PARAMETERS
2454  *
2455  * IN cellHandle - a previously opened cellHandle that corresponds
2456  * to the cell where the group exists.
2457  *
2458  * IN groupName - the group whose members will be returned.
2459  *
2460  * OUT iterationIdP - upon successful completion contains a iterator that
2461  * can be passed to pts_GroupMemberListNext.
2462  *
2463  * LOCKS
2464  *
2465  * No locks are obtained or released by this function
2466  *
2467  * RETURN CODES
2468  *
2469  * Returns != 0 upon successful completion.
2470  *
2471  */
2472
2473 int ADMINAPI
2474 pts_UserMemberListBegin(const void *cellHandle, const char *userName,
2475                         void **iterationIdP, afs_status_p st)
2476 {
2477     return MemberListBegin(cellHandle, userName, ADMPTSUSERNAMENULL,
2478                            ADMPTSUSERNAMETOOLONG, iterationIdP, st);
2479
2480 }
2481
2482 /*
2483  * pts_UserMemberListNext - get the next group a user belongs to
2484  *
2485  * PARAMETERS
2486  *
2487  * IN iterationId - an iterator previously returned by pts_UserMemberListBegin
2488  *
2489  * OUT userName - upon successful completion contains the next group a user
2490  * belongs to.
2491  *
2492  * LOCKS
2493  *
2494  * The iterator mutex is held during the retrieval of the next member.
2495  *
2496  * RETURN CODES
2497  *
2498  * Returns != 0 upon successful completion.
2499  *
2500  */
2501
2502 int ADMINAPI
2503 pts_UserMemberListNext(const void *iterationId, char *userName,
2504                        afs_status_p st)
2505 {
2506     return pts_GroupMemberListNext(iterationId, userName, st);
2507 }
2508
2509 /*
2510  * pts_UserMemberListDone - finish using a user list iterator
2511  *
2512  * PARAMETERS
2513  *
2514  * IN iterationId - an iterator previously returned by pts_UserMemberListBegin
2515  *
2516  * LOCKS
2517  *
2518  * The iterator is locked and then destroyed
2519  *
2520  * RETURN CODES
2521  *
2522  * Returns != 0 upon successful completion.
2523  *
2524  * ASSUMPTIONS
2525  *
2526  * It is the user's responsibility to make sure pts_UserMemberListDone
2527  * is called only once for each iterator.
2528  */
2529
2530 int ADMINAPI
2531 pts_UserMemberListDone(const void *iterationId, afs_status_p st)
2532 {
2533     return pts_GroupMemberListDone(iterationId, st);
2534 }
2535
2536 typedef struct owned_group_list {
2537     namelist owned_names;       /* the list of character names owned by this id */
2538     prlist owned_ids;           /* the list of pts ids owned by this id */
2539     afs_int32 index;            /* the index into owned_names for the next group */
2540     afs_int32 owner;            /* the pts id of the owner */
2541     afs_int32 more;             /* the last parameter to PR_ListOwned */
2542     int finished_retrieving;    /* set when we've processed the last owned_names */
2543     afs_cell_handle_p c_handle; /* ubik client to pts server's from c_handle */
2544     char group[CACHED_ITEMS][PTS_MAX_NAME_LEN]; /* cache of names */
2545 } owned_group_list_t, *owned_group_list_p;
2546
2547 static int
2548 DeleteOwnedGroupSpecificData(void *rpc_specific, afs_status_p st)
2549 {
2550     int rc = 0;
2551     afs_status_t tst = 0;
2552     owned_group_list_p list = (owned_group_list_p) rpc_specific;
2553
2554     if (list->owned_names.namelist_val != NULL) {
2555         free(list->owned_names.namelist_val);
2556     }
2557
2558     if (list->owned_ids.prlist_val != NULL) {
2559         free(list->owned_ids.prlist_val);
2560     }
2561     rc = 1;
2562
2563     if (st != NULL) {
2564         *st = tst;
2565     }
2566     return rc;
2567 }
2568
2569 static int
2570 GetOwnedGroupRPC(void *rpc_specific, int slot, int *last_item,
2571                  int *last_item_contains_data, afs_status_p st)
2572 {
2573     int rc = 0;
2574     afs_status_t tst = 0;
2575     owned_group_list_p list = (owned_group_list_p) rpc_specific;
2576
2577     /*
2578      * We really don't make an rpc for every entry we return here
2579      * since the pts interface allows several members to be returned
2580      * with one rpc, but we fake it to make the iterator happy.
2581      */
2582
2583     /*
2584      * Check to see if we are done retrieving data
2585      */
2586
2587     if ((list->finished_retrieving) && (list->owned_names.namelist_len == 0)) {
2588         *last_item = 1;
2589         *last_item_contains_data = 0;
2590         goto fail_GetOwnedGroupRPC;
2591     }
2592
2593     /*
2594      * Check to see if we really need to make an rpc
2595      */
2596
2597     if ((!list->finished_retrieving) && (list->owned_names.namelist_len == 0)) {
2598         tst =
2599             ubik_PR_ListOwned(list->c_handle->pts, 0, list->owner,
2600                       &list->owned_ids, &list->more);
2601         if (tst != 0) {
2602             goto fail_GetOwnedGroupRPC;
2603         }
2604
2605         if (!TranslatePTSIds
2606             (list->c_handle, &list->owned_names, (idlist *) & list->owned_ids,
2607              &tst)) {
2608             goto fail_GetOwnedGroupRPC;
2609         }
2610         list->index = 0;
2611
2612         if (list->owned_names.namelist_val == NULL) {
2613             *last_item = 1;
2614             *last_item_contains_data = 0;
2615             goto fail_GetOwnedGroupRPC;
2616         }
2617     }
2618
2619     /*
2620      * We can retrieve the next group from data we already received
2621      */
2622
2623     strcpy(list->group[slot], list->owned_names.namelist_val[list->index]);
2624     list->index++;
2625
2626     /*
2627      * Check to see if there is more data to be retrieved
2628      * We need to free up the previously retrieved data here
2629      * and then check to see if the last rpc indicated that there
2630      * were more items to retrieve.
2631      */
2632
2633     if (list->index >= list->owned_names.namelist_len) {
2634         list->owned_names.namelist_len = 0;
2635         free(list->owned_names.namelist_val);
2636         list->owned_names.namelist_val = 0;
2637
2638         list->owned_ids.prlist_len = 0;
2639         free(list->owned_ids.prlist_val);
2640         list->owned_ids.prlist_val = 0;
2641
2642         if (!list->more) {
2643             list->finished_retrieving = 1;
2644         }
2645     }
2646     rc = 1;
2647
2648   fail_GetOwnedGroupRPC:
2649
2650     if (st != NULL) {
2651         *st = tst;
2652     }
2653     return rc;
2654 }
2655
2656 static int
2657 GetOwnedGroupFromCache(void *rpc_specific, int slot, void *dest,
2658                        afs_status_p st)
2659 {
2660     int rc = 0;
2661     afs_status_t tst = 0;
2662     owned_group_list_p list = (owned_group_list_p) rpc_specific;
2663
2664     strcpy((char *)dest, list->group[slot]);
2665     rc = 1;
2666
2667     if (st != NULL) {
2668         *st = tst;
2669     }
2670
2671     return rc;
2672 }
2673
2674 /*
2675  * pts_OwnedGroupListBegin - begin iterating over the list of groups
2676  * a particular user owns.
2677  *
2678  * PARAMETERS
2679  *
2680  * IN cellHandle - a previously opened cellHandle that corresponds
2681  * to the cell where the group exists.
2682  *
2683  * IN ownerName - the owner of the groups of interest.
2684  *
2685  * OUT iterationIdP - upon successful completion contains a iterator that
2686  * can be passed to pts_OwnedGroupListNext.
2687  *
2688  * LOCKS
2689  *
2690  * No locks are held by this function
2691  *
2692  * RETURN CODES
2693  *
2694  * Returns != 0 upon successful completion.
2695  *
2696  */
2697
2698 int ADMINAPI
2699 pts_OwnedGroupListBegin(const void *cellHandle, const char *userName,
2700                         void **iterationIdP, afs_status_p st)
2701 {
2702     int rc = 0;
2703     afs_status_t tst = 0;
2704     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2705     afs_admin_iterator_p iter =
2706         (afs_admin_iterator_p) malloc(sizeof(afs_admin_iterator_t));
2707     owned_group_list_p list =
2708         (owned_group_list_p) malloc(sizeof(owned_group_list_t));
2709
2710     /*
2711      * Validate arguments
2712      */
2713
2714     if (!IsValidCellHandle(c_handle, &tst)) {
2715         goto fail_pts_OwnedGroupListBegin;
2716     }
2717
2718     if ((userName == NULL) || (*userName == 0)) {
2719         tst = ADMPTSUSERNAMENULL;
2720         goto fail_pts_OwnedGroupListBegin;
2721     }
2722
2723     if (iterationIdP == NULL) {
2724         tst = ADMITERATORNULL;
2725         goto fail_pts_OwnedGroupListBegin;
2726     }
2727
2728     if ((iter == NULL) || (list == NULL)) {
2729         tst = ADMNOMEM;
2730         goto fail_pts_OwnedGroupListBegin;
2731     }
2732
2733     /*
2734      * Initialize the iterator specific data
2735      */
2736
2737     list->index = 0;
2738     list->finished_retrieving = 0;
2739     list->c_handle = c_handle;
2740     list->owned_names.namelist_len = 0;
2741     list->owned_names.namelist_val = 0;
2742     list->owned_ids.prlist_len = 0;
2743     list->owned_ids.prlist_val = 0;
2744
2745     /*
2746      * Translate the user name into an id.
2747      */
2748
2749     if (!TranslateOneName
2750         (c_handle, userName, ADMPTSUSERNAMETOOLONG, &list->owner, &tst)) {
2751         goto fail_pts_OwnedGroupListBegin;
2752     }
2753
2754     if (IteratorInit
2755         (iter, (void *)list, GetOwnedGroupRPC, GetOwnedGroupFromCache, NULL,
2756          DeleteOwnedGroupSpecificData, &tst)) {
2757         *iterationIdP = (void *)iter;
2758         rc = 1;
2759     }
2760
2761   fail_pts_OwnedGroupListBegin:
2762
2763     if (rc == 0) {
2764         if (iter != NULL) {
2765             free(iter);
2766         }
2767         if (list != NULL) {
2768             free(list);
2769         }
2770     }
2771
2772     if (st != NULL) {
2773         *st = tst;
2774     }
2775     return rc;
2776 }
2777
2778 /*
2779  * pts_OwnedGroupListNext - get the next group a user owns.
2780  *
2781  * PARAMETERS
2782  *
2783  * IN iterationId - an iterator previously returned by pts_OwnedGroupListBegin
2784  *
2785  * OUT groupName - upon successful completion contains the next group a user
2786  * owns.
2787  *
2788  * LOCKS
2789  *
2790  * The iterator mutex is held during the retrieval of the next member.
2791  *
2792  * RETURN CODES
2793  *
2794  * Returns != 0 upon successful completion.
2795  *
2796  */
2797
2798 int ADMINAPI
2799 pts_OwnedGroupListNext(const void *iterationId, char *groupName,
2800                        afs_status_p st)
2801 {
2802     int rc = 0;
2803     afs_status_t tst = 0;
2804     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
2805
2806     /*
2807      * Validate arguments
2808      */
2809
2810     if (iterationId == NULL) {
2811         tst = ADMITERATORNULL;
2812         goto fail_pts_OwnedGroupListNext;
2813     }
2814
2815     if (groupName == NULL) {
2816         tst = ADMPTSGROUPNAMENULL;
2817         goto fail_pts_OwnedGroupListNext;
2818     }
2819
2820     rc = IteratorNext(iter, (void *)groupName, &tst);
2821
2822   fail_pts_OwnedGroupListNext:
2823
2824     if (st != NULL) {
2825         *st = tst;
2826     }
2827     return rc;
2828 }
2829
2830 /*
2831  * pts_OwnedGroupListDone - finish using a group list iterator
2832  *
2833  * PARAMETERS
2834  *
2835  * IN iterationId - an iterator previously returned by pts_OwnedGroupListBegin
2836  *
2837  * LOCKS
2838  *
2839  * The iterator is locked and then destroyed
2840  *
2841  * RETURN CODES
2842  *
2843  * Returns != 0 upon successful completion.
2844  *
2845  * ASSUMPTIONS
2846  *
2847  * It is the user's responsibility to make sure pts_OwnedGroupListDone
2848  * is called only once for each iterator.
2849  */
2850
2851 int ADMINAPI
2852 pts_OwnedGroupListDone(const void *iterationId, afs_status_p st)
2853 {
2854     int rc = 0;
2855     afs_status_t tst = 0;
2856     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
2857
2858     /*
2859      * Validate arguments
2860      */
2861
2862     if (iterationId == NULL) {
2863         tst = ADMITERATORNULL;
2864         goto fail_pts_OwnedGroupListDone;
2865     }
2866
2867     rc = IteratorDone(iter, &tst);
2868
2869   fail_pts_OwnedGroupListDone:
2870
2871     if (st != NULL) {
2872         *st = tst;
2873     }
2874     return rc;
2875 }
2876
2877 typedef struct pts_list {
2878     prlistentries *names;       /* the current list of pts names in this cell */
2879     prlistentries *currName;    /* the current pts entry */
2880     afs_int32 index;            /* the index into names for the next pts entry */
2881     afs_int32 nextstartindex;   /* the next start index for the RPC */
2882     afs_int32 nentries;         /* the number of entries in names */
2883     afs_int32 flag;             /* the type of the list */
2884     int finished_retrieving;    /* set when we've processed the last owned_names */
2885     afs_cell_handle_p c_handle; /* ubik client to pts server's from c_handle */
2886     char entries[CACHED_ITEMS][PTS_MAX_NAME_LEN];       /* cache of pts names */
2887 } pts_list_t, *pts_list_p;
2888
2889 static int
2890 DeletePTSSpecificData(void *rpc_specific, afs_status_p st)
2891 {
2892     int rc = 0;
2893     afs_status_t tst = 0;
2894     pts_list_p list = (pts_list_p) rpc_specific;
2895
2896     if (list->names) {
2897         free(list->names);
2898     }
2899
2900     rc = 1;
2901
2902     if (st != NULL) {
2903         *st = tst;
2904     }
2905     return rc;
2906 }
2907
2908 static int
2909 GetPTSRPC(void *rpc_specific, int slot, int *last_item,
2910           int *last_item_contains_data, afs_status_p st)
2911 {
2912     int rc = 0;
2913     afs_status_t tst = 0;
2914     pts_list_p list = (pts_list_p) rpc_specific;
2915
2916     /*
2917      * We really don't make an rpc for every entry we return here
2918      * since the pts interface allows several members to be returned
2919      * with one rpc, but we fake it to make the iterator happy.
2920      */
2921
2922     /*
2923      * Check to see if we are done retrieving data
2924      */
2925
2926     if (list->finished_retrieving) {
2927         *last_item = 1;
2928         *last_item_contains_data = 0;
2929         goto fail_GetPTSRPC;
2930     }
2931
2932     /*
2933      * Check to see if we really need to make an rpc
2934      */
2935
2936     if ((!list->finished_retrieving) && (list->index >= list->nentries)) {
2937         afs_int32 start = list->nextstartindex;
2938         prentries bulkentries;
2939         list->nextstartindex = -1;
2940         bulkentries.prentries_val = 0;
2941         bulkentries.prentries_len = 0;
2942
2943         tst =
2944             ubik_PR_ListEntries(list->c_handle->pts, 0, list->flag,
2945                       start, &bulkentries, &(list->nextstartindex));
2946
2947         if (tst != 0) {
2948             goto fail_GetPTSRPC;
2949         }
2950
2951         list->nentries = bulkentries.prentries_len;
2952         list->names = bulkentries.prentries_val;
2953
2954         list->index = 0;
2955         list->currName = list->names;
2956
2957     }
2958
2959     /*
2960      * We can retrieve the next entry from data we already received
2961      */
2962
2963     strcpy(list->entries[slot], list->currName->name);
2964     list->index++;
2965     list->currName++;
2966
2967
2968     /*
2969      * Check to see if there is more data to be retrieved
2970      * We need to free up the previously retrieved data here
2971      * and then check to see if the last rpc indicated that there
2972      * were more items to retrieve.
2973      */
2974
2975     if (list->index >= list->nentries) {
2976         if (list->names) {
2977             free(list->names);
2978         }
2979         list->names = NULL;
2980
2981         if (list->nextstartindex == -1) {
2982             list->finished_retrieving = 1;
2983         }
2984     }
2985     rc = 1;
2986
2987   fail_GetPTSRPC:
2988
2989     if (st != NULL) {
2990         *st = tst;
2991     }
2992
2993     return rc;
2994 }
2995
2996 static int
2997 GetPTSFromCache(void *rpc_specific, int slot, void *dest, afs_status_p st)
2998 {
2999     int rc = 0;
3000     afs_status_t tst = 0;
3001     pts_list_p list = (pts_list_p) rpc_specific;
3002
3003     strcpy((char *)dest, list->entries[slot]);
3004     rc = 1;
3005
3006     if (st != NULL) {
3007         *st = tst;
3008     }
3009
3010     return rc;
3011 }
3012
3013 /*
3014  * pts_UserListBegin - begin iterating over the list of users
3015  * in a particular cell
3016  *
3017  * PARAMETERS
3018  *
3019  * IN cellHandle - a previously opened cellHandle that corresponds
3020  * to the cell where the users exist.
3021  *
3022  * OUT iterationIdP - upon successful completion contains a iterator that
3023  * can be passed to pts_UserListNext.
3024  *
3025  * LOCKS
3026  *
3027  * No locks are held by this function
3028  *
3029  * RETURN CODES
3030  *
3031  * Returns != 0 upon successful completion.
3032  *
3033  */
3034
3035 int ADMINAPI
3036 pts_UserListBegin(const void *cellHandle, void **iterationIdP,
3037                   afs_status_p st)
3038 {
3039     int rc = 0;
3040     afs_status_t tst = 0;
3041     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
3042     afs_admin_iterator_p iter =
3043         (afs_admin_iterator_p) malloc(sizeof(afs_admin_iterator_t));
3044     pts_list_p list = (pts_list_p) malloc(sizeof(pts_list_t));
3045
3046     /*
3047      * Validate arguments
3048      */
3049
3050     if (!IsValidCellHandle(c_handle, &tst)) {
3051         goto fail_pts_UserListBegin;
3052     }
3053
3054     if (iterationIdP == NULL) {
3055         tst = ADMITERATORNULL;
3056         goto fail_pts_UserListBegin;
3057     }
3058
3059     if ((iter == NULL) || (list == NULL)) {
3060         tst = ADMNOMEM;
3061         goto fail_pts_UserListBegin;
3062     }
3063
3064     /*
3065      * Initialize the iterator specific data
3066      */
3067
3068     list->index = 0;
3069     list->finished_retrieving = 0;
3070     list->c_handle = c_handle;
3071     list->names = NULL;
3072     list->nextstartindex = 0;
3073     list->nentries = 0;
3074     list->flag = PRUSERS;
3075
3076     if (IteratorInit
3077         (iter, (void *)list, GetPTSRPC, GetPTSFromCache, NULL,
3078          DeletePTSSpecificData, &tst)) {
3079         *iterationIdP = (void *)iter;
3080         rc = 1;
3081     }
3082
3083   fail_pts_UserListBegin:
3084
3085     if (rc == 0) {
3086         if (iter != NULL) {
3087             free(iter);
3088         }
3089         if (list != NULL) {
3090             free(list);
3091         }
3092     }
3093
3094     if (st != NULL) {
3095         *st = tst;
3096     }
3097     return rc;
3098 }
3099
3100 /*
3101  * pts_UserListNext - get the next user in the cell.
3102  *
3103  * PARAMETERS
3104  *
3105  * IN iterationId - an iterator previously returned by pts_UserListBegin
3106  *
3107  * OUT groupName - upon successful completion contains the next user
3108  *
3109  * LOCKS
3110  *
3111  * The iterator mutex is held during the retrieval of the next member.
3112  *
3113  * RETURN CODES
3114  *
3115  * Returns != 0 upon successful completion.
3116  *
3117  */
3118
3119 int ADMINAPI
3120 pts_UserListNext(const void *iterationId, char *userName, afs_status_p st)
3121 {
3122     int rc = 0;
3123     afs_status_t tst = 0;
3124     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
3125
3126     /*
3127      * Validate arguments
3128      */
3129
3130     if (iterationId == NULL) {
3131         tst = ADMITERATORNULL;
3132         goto fail_pts_UserListNext;
3133     }
3134
3135     if (userName == NULL) {
3136         tst = ADMPTSUSERNAMENULL;
3137         goto fail_pts_UserListNext;
3138     }
3139
3140     rc = IteratorNext(iter, (void *)userName, &tst);
3141
3142   fail_pts_UserListNext:
3143
3144     if (st != NULL) {
3145         *st = tst;
3146     }
3147     return rc;
3148 }
3149
3150 /*
3151  * pts_UserListDone - finish using a user list iterator
3152  *
3153  * PARAMETERS
3154  *
3155  * IN iterationId - an iterator previously returned by pts_UserListBegin
3156  *
3157  * LOCKS
3158  *
3159  * The iterator is locked and then destroyed
3160  *
3161  * RETURN CODES
3162  *
3163  * Returns != 0 upon successful completion.
3164  *
3165  * ASSUMPTIONS
3166  *
3167  * It is the user's responsibility to make sure pts_UserListDone
3168  * is called only once for each iterator.
3169  */
3170
3171 int ADMINAPI
3172 pts_UserListDone(const void *iterationId, afs_status_p st)
3173 {
3174     int rc = 0;
3175     afs_status_t tst = 0;
3176     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
3177
3178     /*
3179      * Validate arguments
3180      */
3181
3182     if (iterationId == NULL) {
3183         tst = ADMITERATORNULL;
3184         goto fail_pts_UserListDone;
3185     }
3186
3187     rc = IteratorDone(iter, &tst);
3188
3189   fail_pts_UserListDone:
3190
3191     if (st != NULL) {
3192         *st = tst;
3193     }
3194     return rc;
3195 }
3196
3197 /*
3198  * pts_GroupListBegin - begin iterating over the list of groups
3199  * in a particular cell.
3200  *
3201  * PARAMETERS
3202  *
3203  * IN cellHandle - a previously opened cellHandle that corresponds
3204  * to the cell where the groups exist.
3205  *
3206  * OUT iterationIdP - upon successful completion contains a iterator that
3207  * can be passed to pts_GroupListNext.
3208  *
3209  * LOCKS
3210  *
3211  * No locks are held by this function
3212  *
3213  * RETURN CODES
3214  *
3215  * Returns != 0 upon successful completion.
3216  *
3217  */
3218
3219 int ADMINAPI
3220 pts_GroupListBegin(const void *cellHandle, void **iterationIdP,
3221                    afs_status_p st)
3222 {
3223     int rc = 0;
3224     afs_status_t tst = 0;
3225     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
3226     afs_admin_iterator_p iter =
3227         (afs_admin_iterator_p) malloc(sizeof(afs_admin_iterator_t));
3228     pts_list_p list = (pts_list_p) malloc(sizeof(pts_list_t));
3229
3230     /*
3231      * Validate arguments
3232      */
3233
3234     if (!IsValidCellHandle(c_handle, &tst)) {
3235         goto fail_pts_GroupListBegin;
3236     }
3237
3238     if (iterationIdP == NULL) {
3239         tst = ADMITERATORNULL;
3240         goto fail_pts_GroupListBegin;
3241     }
3242
3243     if ((iter == NULL) || (list == NULL)) {
3244         tst = ADMNOMEM;
3245         goto fail_pts_GroupListBegin;
3246     }
3247
3248     /*
3249      * Initialize the iterator specific data
3250      */
3251
3252     list->index = 0;
3253     list->finished_retrieving = 0;
3254     list->c_handle = c_handle;
3255     list->names = NULL;
3256     list->nextstartindex = 0;
3257     list->nentries = 0;
3258     list->flag = PRGROUPS;
3259
3260     if (IteratorInit
3261         (iter, (void *)list, GetPTSRPC, GetPTSFromCache, NULL,
3262          DeletePTSSpecificData, &tst)) {
3263         *iterationIdP = (void *)iter;
3264         rc = 1;
3265     }
3266
3267   fail_pts_GroupListBegin:
3268
3269     if (rc == 0) {
3270         if (iter != NULL) {
3271             free(iter);
3272         }
3273         if (list != NULL) {
3274             free(list);
3275         }
3276     }
3277
3278     if (st != NULL) {
3279         *st = tst;
3280     }
3281     return rc;
3282 }
3283
3284 /*
3285  * pts_UserListNext - get the next group in a cell.
3286  *
3287  * PARAMETERS
3288  *
3289  * IN iterationId - an iterator previously returned by pts_GroupListBegin
3290  *
3291  * OUT groupName - upon successful completion contains the next group
3292  *
3293  * LOCKS
3294  *
3295  * The iterator mutex is held during the retrieval of the next member.
3296  *
3297  * RETURN CODES
3298  *
3299  * Returns != 0 upon successful completion.
3300  *
3301  */
3302
3303 int ADMINAPI
3304 pts_GroupListNext(const void *iterationId, char *groupName, afs_status_p st)
3305 {
3306     int rc = 0;
3307     afs_status_t tst = 0;
3308     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
3309
3310     /*
3311      * Validate arguments
3312      */
3313
3314     if (iterationId == NULL) {
3315         tst = ADMITERATORNULL;
3316         goto fail_pts_GroupListNext;
3317     }
3318
3319     if (groupName == NULL) {
3320         tst = ADMPTSGROUPNAMENULL;
3321         goto fail_pts_GroupListNext;
3322     }
3323
3324     rc = IteratorNext(iter, (void *)groupName, &tst);
3325
3326   fail_pts_GroupListNext:
3327
3328     if (st != NULL) {
3329         *st = tst;
3330     }
3331     return rc;
3332 }
3333
3334 /*
3335  * pts_GroupListDone - finish using a group list iterator
3336  *
3337  * PARAMETERS
3338  *
3339  * IN iterationId - an iterator previously returned by pts_GroupListBegin
3340  *
3341  * LOCKS
3342  *
3343  * The iterator is locked and then destroyed
3344  *
3345  * RETURN CODES
3346  *
3347  * Returns != 0 upon successful completion.
3348  *
3349  * ASSUMPTIONS
3350  *
3351  * It is the user's responsibility to make sure pts_GroupListDone
3352  * is called only once for each iterator.
3353  */
3354
3355 int ADMINAPI
3356 pts_GroupListDone(const void *iterationId, afs_status_p st)
3357 {
3358     int rc = 0;
3359     afs_status_t tst = 0;
3360     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
3361
3362     /*
3363      * Validate arguments
3364      */
3365
3366     if (iterationId == NULL) {
3367         tst = ADMITERATORNULL;
3368         goto fail_pts_GroupListDone;
3369     }
3370
3371     rc = IteratorDone(iter, &tst);
3372
3373   fail_pts_GroupListDone:
3374
3375     if (st != NULL) {
3376         *st = tst;
3377     }
3378     return rc;
3379 }