1 /* nss_cdb.c: nss_cdb common routines.
2 *
3 * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
4 * Public domain.
5 */
6
7 #include "nss_cdb.h"
8 #include "cdb_int.h" /* for internal_function */
9 #include <string.h>
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <errno.h>
13 #include <stdio.h>
14
15 #if __GLIBC__ /* XXX this is in fact not a right condition */
16 /* XXX on glibc, this stuff works due to linker/libpthreads stubs/tricks.
17 * On other libcs, it may require linking whole -lpthread, which is
18 * not a good thing to do for nss module...
19 */
20
21 #include <pthread.h>
22
23 #define lock_define(class,name) \
24 class pthread_mutex_t name = PTHREAD_MUTEX_INITIALIZER;
25 #define lock_lock(name) pthread_mutex_lock(&(name))
26 #define lock_unlock(name) pthread_mutex_unlock(&(name))
27
28 #else /* !__GNU_LIBRARY__ */
29
30 # define lock_define_initialized(class,name)
31 # define lock_lock(name)
32 # define lock_unlock(name)
33
34 #endif /* __GNU_LIBRARY__ */
35
lock_define(static,lock)36 lock_define(static, lock)
37
38 /* General principle: we skip invalid/unparseable entries completely,
39 * as if there was no such entry at all (returning NOTFOUND).
40 * In case of data read error (e.g. invalid .cdb structure), we
41 * return UNAVAIL.
42 */
43
44 #define isopen(dbp) ((dbp)->lastpos)
45
46 static int
47 __nss_cdb_dosetent(struct nss_cdb *dbp) {
48 int fd;
49
50 fd = open(dbp->dbname, O_RDONLY);
51 if (fd < 0)
52 return 0;
53 if (cdb_init(&dbp->cdb, fd) != 0) {
54 close(fd);
55 return 0;
56 }
57 close(fd);
58 dbp->lastpos = 2048; /* cdb_seqinit() */
59 return 1;
60 }
61
62 static void
__nss_cdb_doendent(struct nss_cdb * dbp)63 __nss_cdb_doendent(struct nss_cdb *dbp) {
64 cdb_free(&dbp->cdb);
65 dbp->lastpos = 0;
66 dbp->keepopen = 0;
67 }
68
69 enum nss_status internal_function
__nss_cdb_setent(struct nss_cdb * dbp,int stayopen)70 __nss_cdb_setent(struct nss_cdb *dbp, int stayopen) {
71 enum nss_status r;
72 lock_lock(lock);
73 if (isopen(dbp) || __nss_cdb_dosetent(dbp))
74 r = NSS_STATUS_SUCCESS, dbp->keepopen |= stayopen;
75 else
76 r = NSS_STATUS_UNAVAIL;
77 lock_unlock(lock);
78 return r;
79 }
80
81 enum nss_status internal_function
__nss_cdb_endent(struct nss_cdb * dbp)82 __nss_cdb_endent(struct nss_cdb *dbp) {
83 lock_lock(lock);
84 if (isopen(dbp))
85 __nss_cdb_doendent(dbp);
86 lock_unlock(lock);
87 return NSS_STATUS_SUCCESS;
88 }
89
90 static enum nss_status
__nss_cdb_dobyname(struct nss_cdb * dbp,const char * key,unsigned len,void * result,char * buf,size_t bufl,int * errnop)91 __nss_cdb_dobyname(struct nss_cdb *dbp, const char *key, unsigned len,
92 void *result, char *buf, size_t bufl, int *errnop) {
93 int r;
94
95 if ((r = cdb_find(&dbp->cdb, key, len)) < 0)
96 return *errnop = errno, NSS_STATUS_UNAVAIL;
97 len = cdb_datalen(&dbp->cdb);
98 if (!r || len < 2)
99 return *errnop = ENOENT, NSS_STATUS_NOTFOUND;
100 if (len >= bufl)
101 return *errnop = ERANGE, NSS_STATUS_TRYAGAIN;
102 if (cdb_read(&dbp->cdb, buf, len, cdb_datapos(&dbp->cdb)) != 0)
103 return *errnop = errno, NSS_STATUS_UNAVAIL;
104 buf[len] = '\0';
105 if ((r = dbp->parsefn(result, buf, bufl)) < 0)
106 return *errnop = ENOENT, NSS_STATUS_NOTFOUND;
107 if (!r)
108 return *errnop = ERANGE, NSS_STATUS_TRYAGAIN;
109
110 return NSS_STATUS_SUCCESS;
111 }
112
113 enum nss_status internal_function
__nss_cdb_byname(struct nss_cdb * dbp,const char * name,void * result,char * buf,size_t bufl,int * errnop)114 __nss_cdb_byname(struct nss_cdb *dbp, const char *name,
115 void *result, char *buf, size_t bufl, int *errnop) {
116 enum nss_status r;
117 if (*name == ':')
118 return *errnop = ENOENT, NSS_STATUS_NOTFOUND;
119 lock_lock(lock);
120 if (!isopen(dbp) && !__nss_cdb_dosetent(dbp))
121 *errnop = errno, r = NSS_STATUS_UNAVAIL;
122 else {
123 r = __nss_cdb_dobyname(dbp, name, strlen(name), result, buf, bufl, errnop);
124 if (!dbp->keepopen)
125 __nss_cdb_doendent(dbp);
126 }
127 lock_unlock(lock);
128 return r;
129 }
130
131 static enum nss_status
__nss_cdb_dobyid(struct nss_cdb * dbp,unsigned long id,void * result,char * buf,size_t bufl,int * errnop)132 __nss_cdb_dobyid(struct nss_cdb *dbp, unsigned long id,
133 void *result, char *buf, size_t bufl, int *errnop) {
134 int r;
135 unsigned len;
136 const char *data;
137
138 if ((r = cdb_find(&dbp->cdb, buf, sprintf(buf, ":%lu", id))) < 0)
139 return *errnop = errno, NSS_STATUS_UNAVAIL;
140 len = cdb_datalen(&dbp->cdb);
141 if (!r || len < 2)
142 return *errnop = ENOENT, NSS_STATUS_NOTFOUND;
143 if (!(data = (const char*)cdb_get(&dbp->cdb, len, cdb_datapos(&dbp->cdb))))
144 return *errnop = errno, NSS_STATUS_UNAVAIL;
145
146 return __nss_cdb_dobyname(dbp, data, len, result, buf, bufl, errnop);
147 }
148
149 enum nss_status internal_function
__nss_cdb_byid(struct nss_cdb * dbp,unsigned long id,void * result,char * buf,size_t bufl,int * errnop)150 __nss_cdb_byid(struct nss_cdb *dbp, unsigned long id,
151 void *result, char *buf, size_t bufl, int *errnop) {
152 enum nss_status r;
153 if (bufl < 30)
154 return *errnop = ERANGE, NSS_STATUS_TRYAGAIN;
155 lock_lock(lock);
156 if (!isopen(dbp) && !__nss_cdb_dosetent(dbp))
157 *errnop = errno, r = NSS_STATUS_UNAVAIL;
158 else {
159 r = __nss_cdb_dobyid(dbp, id, result, buf, bufl, errnop);
160 if (!dbp->keepopen)
161 __nss_cdb_doendent(dbp);
162 }
163 lock_unlock(lock);
164 return r;
165 }
166
167 static enum nss_status
__nss_cdb_dogetent(struct nss_cdb * dbp,void * result,char * buf,size_t bufl,int * errnop)168 __nss_cdb_dogetent(struct nss_cdb *dbp,
169 void *result, char *buf, size_t bufl, int *errnop) {
170 int r;
171 unsigned lastpos;
172
173 if (!isopen(dbp) && !__nss_cdb_dosetent(dbp))
174 return *errnop = errno, NSS_STATUS_UNAVAIL;
175
176 while((lastpos = dbp->lastpos, r = cdb_seqnext(&dbp->lastpos, &dbp->cdb)) > 0)
177 {
178 if (cdb_keylen(&dbp->cdb) < 2) continue;
179 if (((const char *)cdb_getkey(&dbp->cdb))[0] == ':') /* can't fail */
180 continue;
181 if (cdb_datalen(&dbp->cdb) >= bufl)
182 return dbp->lastpos = lastpos, *errnop = ERANGE, NSS_STATUS_TRYAGAIN;
183 cdb_readdata(&dbp->cdb, buf);
184 buf[cdb_datalen(&dbp->cdb)] = '\0';
185 if ((r = dbp->parsefn(result, buf, bufl)) == 0)
186 return dbp->lastpos = lastpos, *errnop = ERANGE, NSS_STATUS_TRYAGAIN;
187 if (r > 0)
188 return NSS_STATUS_SUCCESS;
189 }
190 if (r < 0)
191 return *errnop = errno, NSS_STATUS_UNAVAIL;
192 else
193 return *errnop = ENOENT, NSS_STATUS_NOTFOUND;
194 }
195
196 enum nss_status internal_function
__nss_cdb_getent(struct nss_cdb * dbp,void * result,char * buf,size_t bufl,int * errnop)197 __nss_cdb_getent(struct nss_cdb *dbp,
198 void *result, char *buf, size_t bufl, int *errnop) {
199 enum nss_status r;
200 if (bufl < 30)
201 return *errnop = ERANGE, NSS_STATUS_TRYAGAIN;
202 lock_lock(lock);
203 dbp->keepopen |= 1;
204 r = __nss_cdb_dogetent(dbp, result, buf, bufl, errnop);
205 lock_unlock(lock);
206 return r;
207 }
208
209