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