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