* lib/idcache.c: Restore most of the 2006-11-06 patch, so as to
[pspp] / lib / idcache.c
1 /* idcache.c -- map user and group IDs, cached for speed
2
3    Copyright (C) 1985, 1988, 1989, 1990, 1997, 1998, 2003, 2005, 2006
4    Free Software Foundation, Inc.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software Foundation,
18    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
19
20 #include <config.h>
21
22 #include <stddef.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <pwd.h>
27 #include <grp.h>
28
29 #include <unistd.h>
30
31 #include "xalloc.h"
32
33 #ifdef __DJGPP__
34 static char digits[] = "0123456789";
35 #endif
36
37 struct userid
38 {
39   union
40     {
41       uid_t u;
42       gid_t g;
43     } id;
44   struct userid *next;
45   char name[FLEXIBLE_ARRAY_MEMBER];
46 };
47
48 static struct userid *user_alist;
49
50 /* The members of this list have names not in the local passwd file.  */
51 static struct userid *nouser_alist;
52
53 /* Translate UID to a login name, with cache, or NULL if unresolved.  */
54
55 char *
56 getuser (uid_t uid)
57 {
58   struct userid *tail;
59   struct userid *match = NULL;
60
61   for (tail = user_alist; tail; tail = tail->next)
62     {
63       if (tail->id.u == uid)
64         {
65           match = tail;
66           break;
67         }
68     }
69
70   if (match == NULL)
71     {
72       struct passwd *pwent = getpwuid (uid);
73       char const *name = pwent ? pwent->pw_name : "";
74       match = xmalloc (offsetof (struct userid, name) + strlen (name) + 1);
75       match->id.u = uid;
76       strcpy (match->name, name);
77
78       /* Add to the head of the list, so most recently used is first.  */
79       match->next = user_alist;
80       user_alist = match;
81     }
82
83   return match->name[0] ? match->name : NULL;
84 }
85
86 /* Translate USER to a UID, with cache.
87    Return NULL if there is no such user.
88    (We also cache which user names have no passwd entry,
89    so we don't keep looking them up.)  */
90
91 uid_t *
92 getuidbyname (const char *user)
93 {
94   struct userid *tail;
95   struct passwd *pwent;
96
97   for (tail = user_alist; tail; tail = tail->next)
98     /* Avoid a function call for the most common case.  */
99     if (*tail->name == *user && !strcmp (tail->name, user))
100       return &tail->id.u;
101
102   for (tail = nouser_alist; tail; tail = tail->next)
103     /* Avoid a function call for the most common case.  */
104     if (*tail->name == *user && !strcmp (tail->name, user))
105       return NULL;
106
107   pwent = getpwnam (user);
108 #ifdef __DJGPP__
109   /* We need to pretend to be the user USER, to make
110      pwd functions know about an arbitrary user name.  */
111   if (!pwent && strspn (user, digits) < strlen (user))
112     {
113       setenv ("USER", user, 1);
114       pwent = getpwnam (user);  /* now it will succeed */
115     }
116 #endif
117
118   tail = xmalloc (offsetof (struct userid, name) + strlen (user) + 1);
119   strcpy (tail->name, user);
120
121   /* Add to the head of the list, so most recently used is first.  */
122   if (pwent)
123     {
124       tail->id.u = pwent->pw_uid;
125       tail->next = user_alist;
126       user_alist = tail;
127       return &tail->id.u;
128     }
129
130   tail->next = nouser_alist;
131   nouser_alist = tail;
132   return NULL;
133 }
134
135 /* Use the same struct as for userids.  */
136 static struct userid *group_alist;
137 static struct userid *nogroup_alist;
138
139 /* Translate GID to a group name, with cache, or NULL if unresolved.  */
140
141 char *
142 getgroup (gid_t gid)
143 {
144   struct userid *tail;
145   struct userid *match = NULL;
146
147   for (tail = group_alist; tail; tail = tail->next)
148     {
149       if (tail->id.g == gid)
150         {
151           match = tail;
152           break;
153         }
154     }
155
156   if (match == NULL)
157     {
158       struct group *grent = getgrgid (gid);
159       char const *name = grent ? grent->gr_name : "";
160       match = xmalloc (offsetof (struct userid, name) + strlen (name) + 1);
161       match->id.g = gid;
162       strcpy (match->name, name);
163
164       /* Add to the head of the list, so most recently used is first.  */
165       match->next = group_alist;
166       group_alist = match;
167     }
168
169   return match->name[0] ? match->name : NULL;
170 }
171
172 /* Translate GROUP to a GID, with cache.
173    Return NULL if there is no such group.
174    (We also cache which group names have no group entry,
175    so we don't keep looking them up.)  */
176
177 gid_t *
178 getgidbyname (const char *group)
179 {
180   struct userid *tail;
181   struct group *grent;
182
183   for (tail = group_alist; tail; tail = tail->next)
184     /* Avoid a function call for the most common case.  */
185     if (*tail->name == *group && !strcmp (tail->name, group))
186       return &tail->id.g;
187
188   for (tail = nogroup_alist; tail; tail = tail->next)
189     /* Avoid a function call for the most common case.  */
190     if (*tail->name == *group && !strcmp (tail->name, group))
191       return NULL;
192
193   grent = getgrnam (group);
194 #ifdef __DJGPP__
195   /* We need to pretend to belong to group GROUP, to make
196      grp functions know about an arbitrary group name.  */
197   if (!grent && strspn (group, digits) < strlen (group))
198     {
199       setenv ("GROUP", group, 1);
200       grent = getgrnam (group); /* now it will succeed */
201     }
202 #endif
203
204   tail = xmalloc (offsetof (struct userid, name) + strlen (group) + 1);
205   strcpy (tail->name, group);
206
207   /* Add to the head of the list, so most recently used is first.  */
208   if (grent)
209     {
210       tail->id.g = grent->gr_gid;
211       tail->next = group_alist;
212       group_alist = tail;
213       return &tail->id.g;
214     }
215
216   tail->next = nogroup_alist;
217   nogroup_alist = tail;
218   return NULL;
219 }