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