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