Tizen 2.0 Release
[external/tizen-coreutils.git] / 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 /* Each entry on list is a user name for which the first lookup failed.  */
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
138 /* Each entry on list is a group name for which the first lookup failed.  */
139 static struct userid *nogroup_alist;
140
141 /* Translate GID to a group name, with cache, or NULL if unresolved.  */
142
143 char *
144 getgroup (gid_t gid)
145 {
146   struct userid *tail;
147   struct userid *match = NULL;
148
149   for (tail = group_alist; tail; tail = tail->next)
150     {
151       if (tail->id.g == gid)
152         {
153           match = tail;
154           break;
155         }
156     }
157
158   if (match == NULL)
159     {
160       struct group *grent = getgrgid (gid);
161       char const *name = grent ? grent->gr_name : "";
162       match = xmalloc (offsetof (struct userid, name) + strlen (name) + 1);
163       match->id.g = gid;
164       strcpy (match->name, name);
165
166       /* Add to the head of the list, so most recently used is first.  */
167       match->next = group_alist;
168       group_alist = match;
169     }
170
171   return match->name[0] ? match->name : NULL;
172 }
173
174 /* Translate GROUP to a GID, with cache.
175    Return NULL if there is no such group.
176    (We also cache which group names have no group entry,
177    so we don't keep looking them up.)  */
178
179 gid_t *
180 getgidbyname (const char *group)
181 {
182   struct userid *tail;
183   struct group *grent;
184
185   for (tail = group_alist; tail; tail = tail->next)
186     /* Avoid a function call for the most common case.  */
187     if (*tail->name == *group && !strcmp (tail->name, group))
188       return &tail->id.g;
189
190   for (tail = nogroup_alist; tail; tail = tail->next)
191     /* Avoid a function call for the most common case.  */
192     if (*tail->name == *group && !strcmp (tail->name, group))
193       return NULL;
194
195   grent = getgrnam (group);
196 #ifdef __DJGPP__
197   /* We need to pretend to belong to group GROUP, to make
198      grp functions know about an arbitrary group name.  */
199   if (!grent && strspn (group, digits) < strlen (group))
200     {
201       setenv ("GROUP", group, 1);
202       grent = getgrnam (group); /* now it will succeed */
203     }
204 #endif
205
206   tail = xmalloc (offsetof (struct userid, name) + strlen (group) + 1);
207   strcpy (tail->name, group);
208
209   /* Add to the head of the list, so most recently used is first.  */
210   if (grent)
211     {
212       tail->id.g = grent->gr_gid;
213       tail->next = group_alist;
214       group_alist = tail;
215       return &tail->id.g;
216     }
217
218   tail->next = nogroup_alist;
219   nogroup_alist = tail;
220   return NULL;
221 }