]> granicus.if.org Git - shadow/blob - lib/commonio.c
updated to 360t71f. Thanks to Leandro Azevedo <leorock182@gmail.com>.
[shadow] / lib / commonio.c
1
2 #include <config.h>
3
4 #ident "$Id$"
5
6 #include "defines.h"
7 #include <sys/stat.h>
8 #include <stdlib.h>
9 #include <limits.h>
10 #include <utime.h>
11 #include <fcntl.h>
12 #include <errno.h>
13 #include <stdio.h>
14 #include <signal.h>
15 #include "nscd.h"
16 #ifdef WITH_SELINUX
17 #include <selinux/selinux.h>
18 static security_context_t old_context = NULL;
19 #endif
20 #include "commonio.h"
21
22 /* local function prototypes */
23 static int lrename (const char *, const char *);
24 static int check_link_count (const char *);
25 static int do_lock_file (const char *, const char *);
26 static FILE *fopen_set_perms (const char *, const char *, const struct stat *);
27 static int create_backup (const char *, FILE *);
28 static void free_linked_list (struct commonio_db *);
29 static void add_one_entry (struct commonio_db *, struct commonio_entry *);
30 static int name_is_nis (const char *);
31 static int write_all (const struct commonio_db *);
32 static struct commonio_entry *find_entry_by_name (struct commonio_db *,
33                                                   const char *);
34 static struct commonio_entry *next_entry_by_name (struct commonio_db *,
35                                                   struct commonio_entry *pos,
36                                                   const char *);
37
38 static int lock_count = 0;
39 static int nscd_need_reload = 0;
40
41 /*
42  * Simple rename(P) alternative that attempts to rename to symlink
43  * target.
44  */
45 int lrename (const char *old, const char *new)
46 {
47
48         char resolved_path[PATH_MAX];
49         int res;
50
51 #if defined(S_ISLNK)
52         struct stat sb;
53         if (lstat (new, &sb) == 0 && S_ISLNK (sb.st_mode)) {
54                 if (realpath (new, resolved_path) == NULL) {
55                         perror ("realpath in lrename()");
56                 } else {
57                         new = resolved_path;
58                 }
59         }
60 #endif
61         res = rename (old, new);
62         return res;
63 }
64
65 static int check_link_count (const char *file)
66 {
67         struct stat sb;
68
69         if (stat (file, &sb) != 0)
70                 return 0;
71
72         if (sb.st_nlink != 2)
73                 return 0;
74
75         return 1;
76 }
77
78
79 static int do_lock_file (const char *file, const char *lock)
80 {
81         int fd;
82         int pid;
83         int len;
84         int retval;
85         char buf[32];
86
87         if ((fd = open (file, O_CREAT | O_EXCL | O_WRONLY, 0600)) == -1)
88                 return 0;
89
90         pid = getpid ();
91         snprintf (buf, sizeof buf, "%d", pid);
92         len = strlen (buf) + 1;
93         if (write (fd, buf, len) != len) {
94                 close (fd);
95                 unlink (file);
96                 return 0;
97         }
98         close (fd);
99
100         if (link (file, lock) == 0) {
101                 retval = check_link_count (file);
102                 unlink (file);
103                 return retval;
104         }
105
106         if ((fd = open (lock, O_RDWR)) == -1) {
107                 unlink (file);
108                 errno = EINVAL;
109                 return 0;
110         }
111         len = read (fd, buf, sizeof (buf) - 1);
112         close (fd);
113         if (len <= 0) {
114                 unlink (file);
115                 errno = EINVAL;
116                 return 0;
117         }
118         buf[len] = '\0';
119         if ((pid = strtol (buf, (char **) 0, 10)) == 0) {
120                 unlink (file);
121                 errno = EINVAL;
122                 return 0;
123         }
124         if (kill (pid, 0) == 0) {
125                 unlink (file);
126                 errno = EEXIST;
127                 return 0;
128         }
129         if (unlink (lock) != 0) {
130                 unlink (file);
131                 return 0;
132         }
133
134         retval = 0;
135         if (link (file, lock) == 0 && check_link_count (file))
136                 retval = 1;
137
138         unlink (file);
139         return retval;
140 }
141
142
143 static FILE *fopen_set_perms (const char *name, const char *mode,
144                               const struct stat *sb)
145 {
146         FILE *fp;
147         mode_t mask;
148
149         mask = umask (0777);
150         fp = fopen (name, mode);
151         umask (mask);
152         if (!fp)
153                 return NULL;
154
155 #ifdef HAVE_FCHOWN
156         if (fchown (fileno (fp), sb->st_uid, sb->st_gid))
157                 goto fail;
158 #else
159         if (chown (name, sb->st_mode))
160                 goto fail;
161 #endif
162
163 #ifdef HAVE_FCHMOD
164         if (fchmod (fileno (fp), sb->st_mode & 0664))
165                 goto fail;
166 #else
167         if (chmod (name, sb->st_mode & 0664))
168                 goto fail;
169 #endif
170         return fp;
171
172       fail:
173         fclose (fp);
174         unlink (name);
175         return NULL;
176 }
177
178
179 static int create_backup (const char *backup, FILE * fp)
180 {
181         struct stat sb;
182         struct utimbuf ub;
183         FILE *bkfp;
184         int c;
185         mode_t mask;
186
187         if (fstat (fileno (fp), &sb))
188                 return -1;
189
190         mask = umask (077);
191         bkfp = fopen (backup, "w");
192         umask (mask);
193         if (!bkfp)
194                 return -1;
195
196         /* TODO: faster copy, not one-char-at-a-time.  --marekm */
197         c = 0;
198         if (fseek (fp, 0, SEEK_SET) == 0)
199                 while ((c = getc (fp)) != EOF) {
200                         if (putc (c, bkfp) == EOF)
201                                 break;
202                 }
203         if (c != EOF || ferror (fp) || fflush (bkfp)) {
204                 fclose (bkfp);
205                 return -1;
206         }
207         if (fclose (bkfp))
208                 return -1;
209
210         ub.actime = sb.st_atime;
211         ub.modtime = sb.st_mtime;
212         utime (backup, &ub);
213         return 0;
214 }
215
216
217 static void free_linked_list (struct commonio_db *db)
218 {
219         struct commonio_entry *p;
220
221         while (db->head) {
222                 p = db->head;
223                 db->head = p->next;
224
225                 if (p->line)
226                         free (p->line);
227
228                 if (p->eptr)
229                         db->ops->free (p->eptr);
230
231                 free (p);
232         }
233         db->tail = NULL;
234 }
235
236
237 int commonio_setname (struct commonio_db *db, const char *name)
238 {
239         snprintf (db->filename, sizeof (db->filename), "%s", name);
240         return 1;
241 }
242
243
244 int commonio_present (const struct commonio_db *db)
245 {
246         return (access (db->filename, F_OK) == 0);
247 }
248
249
250 int commonio_lock_nowait (struct commonio_db *db)
251 {
252         char file[1024];
253         char lock[1024];
254
255         if (db->locked)
256                 return 1;
257
258         snprintf (file, sizeof file, "%s.%ld", db->filename, (long) getpid ());
259         snprintf (lock, sizeof lock, "%s.lock", db->filename);
260         if (do_lock_file (file, lock)) {
261                 db->locked = 1;
262                 lock_count++;
263                 return 1;
264         }
265         return 0;
266 }
267
268
269 int commonio_lock (struct commonio_db *db)
270 {
271 #ifdef HAVE_LCKPWDF
272         /*
273          * only if the system libc has a real lckpwdf() - the one from
274          * lockpw.c calls us and would cause infinite recursion!
275          */
276
277         /*
278          * Call lckpwdf() on the first lock.
279          * If it succeeds, call *_lock() only once
280          * (no retries, it should always succeed).
281          */
282         if (lock_count == 0) {
283                 if (lckpwdf () == -1)
284                         return 0;       /* failure */
285         }
286
287         if (commonio_lock_nowait (db))
288                 return 1;       /* success */
289
290         ulckpwdf ();
291         return 0;               /* failure */
292 #else
293         int i;
294
295         /*
296          * lckpwdf() not used - do it the old way.
297          */
298 #ifndef LOCK_TRIES
299 #define LOCK_TRIES 15
300 #endif
301
302 #ifndef LOCK_SLEEP
303 #define LOCK_SLEEP 1
304 #endif
305         for (i = 0; i < LOCK_TRIES; i++) {
306                 if (i > 0)
307                         sleep (LOCK_SLEEP);     /* delay between retries */
308                 if (commonio_lock_nowait (db))
309                         return 1;       /* success */
310                 /* no unnecessary retries on "permission denied" errors */
311                 if (geteuid () != 0)
312                         return 0;
313         }
314         return 0;               /* failure */
315 #endif
316 }
317
318 static void dec_lock_count (void)
319 {
320         if (lock_count > 0) {
321                 lock_count--;
322                 if (lock_count == 0) {
323                         /* Tell nscd when lock count goes to zero,
324                            if any of the files were changed.  */
325                         if (nscd_need_reload) {
326                                 nscd_flush_cache ("passwd");
327                                 nscd_flush_cache ("group");
328                                 nscd_need_reload = 0;
329                         }
330 #ifdef HAVE_LCKPWDF
331                         ulckpwdf ();
332 #endif
333                 }
334         }
335 }
336
337
338 int commonio_unlock (struct commonio_db *db)
339 {
340         char lock[1024];
341
342         if (db->isopen) {
343                 db->readonly = 1;
344                 if (!commonio_close (db)) {
345                         if (db->locked)
346                                 dec_lock_count ();
347                         return 0;
348                 }
349         }
350         if (db->locked) {
351                 /*
352                  * Unlock in reverse order: remove the lock file,
353                  * then call ulckpwdf() (if used) on last unlock.
354                  */
355                 db->locked = 0;
356                 snprintf (lock, sizeof lock, "%s.lock", db->filename);
357                 unlink (lock);
358                 dec_lock_count ();
359                 return 1;
360         }
361         return 0;
362 }
363
364
365 static void add_one_entry (struct commonio_db *db, struct commonio_entry *p)
366 {
367         p->next = NULL;
368         p->prev = db->tail;
369         if (!db->head)
370                 db->head = p;
371         if (db->tail)
372                 db->tail->next = p;
373         db->tail = p;
374 }
375
376
377 static int name_is_nis (const char *n)
378 {
379         return (n[0] == '+' || n[0] == '-');
380 }
381
382
383 /*
384  * New entries are inserted before the first NIS entry.  Order is preserved
385  * when db is written out.
386  */
387 #ifndef KEEP_NIS_AT_END
388 #define KEEP_NIS_AT_END 1
389 #endif
390
391 #if KEEP_NIS_AT_END
392 static void add_one_entry_nis (struct commonio_db *, struct commonio_entry *);
393
394 /*
395  * Insert an entry between the regular entries, and the NIS entries.
396  */
397 static void
398 add_one_entry_nis (struct commonio_db *db, struct commonio_entry *newp)
399 {
400         struct commonio_entry *p;
401
402         for (p = db->head; p; p = p->next) {
403                 if (name_is_nis
404                     (p->eptr ? db->ops->getname (p->eptr) : p->line)) {
405                         newp->next = p;
406                         newp->prev = p->prev;
407                         if (p->prev)
408                                 p->prev->next = newp;
409                         else
410                                 db->head = newp;
411                         p->prev = newp;
412                         return;
413                 }
414         }
415         add_one_entry (db, newp);
416 }
417 #endif                          /* KEEP_NIS_AT_END */
418
419 /* Initial buffer size, as well as increment if not sufficient
420    (for reading very long lines in group files).  */
421 #define BUFLEN 4096
422
423 int commonio_open (struct commonio_db *db, int mode)
424 {
425         char *buf;
426         char *cp;
427         char *line;
428         struct commonio_entry *p;
429         void *eptr;
430         int flags = mode;
431         int buflen;
432         int saved_errno;
433
434         mode &= ~O_CREAT;
435
436         if (db->isopen || (mode != O_RDONLY && mode != O_RDWR)) {
437                 errno = EINVAL;
438                 return 0;
439         }
440         db->readonly = (mode == O_RDONLY);
441         if (!db->readonly && !db->locked) {
442                 errno = EACCES;
443                 return 0;
444         }
445
446         db->head = db->tail = db->cursor = NULL;
447         db->changed = 0;
448
449         db->fp = fopen (db->filename, db->readonly ? "r" : "r+");
450
451         /*
452          * If O_CREAT was specified and the file didn't exist, it will be
453          * created by commonio_close().  We have no entries to read yet.  --marekm
454          */
455         if (!db->fp) {
456                 if ((flags & O_CREAT) && errno == ENOENT) {
457                         db->isopen = 1;
458                         return 1;
459                 }
460                 return 0;
461         }
462
463         /* Do not inherit fd in spawned processes (e.g. nscd) */
464         fcntl(fileno(db->fp), F_SETFD, FD_CLOEXEC);
465
466 #ifdef WITH_SELINUX
467         db->scontext = NULL;
468         if ((is_selinux_enabled () > 0) && (!db->readonly)) {
469                 if (fgetfilecon (fileno (db->fp), &db->scontext) < 0) {
470                         goto cleanup_errno;
471                 }
472         }
473 #endif
474
475         buflen = BUFLEN;
476         buf = (char *) malloc (buflen);
477         if (!buf)
478                 goto cleanup_ENOMEM;
479
480         while (db->ops->fgets (buf, buflen, db->fp)) {
481                 while (!(cp = strrchr (buf, '\n')) && !feof (db->fp)) {
482                         int len;
483
484                         buflen += BUFLEN;
485                         cp = (char *) realloc (buf, buflen);
486                         if (!cp)
487                                 goto cleanup_buf;
488                         buf = cp;
489                         len = strlen (buf);
490                         db->ops->fgets (buf + len, buflen - len, db->fp);
491                 }
492                 if ((cp = strrchr (buf, '\n')))
493                         *cp = '\0';
494
495                 if (!(line = strdup (buf)))
496                         goto cleanup_buf;
497
498                 if (name_is_nis (line)) {
499                         eptr = NULL;
500                 } else if ((eptr = db->ops->parse (line))) {
501                         eptr = db->ops->dup (eptr);
502                         if (!eptr)
503                                 goto cleanup_line;
504                 }
505
506                 p = (struct commonio_entry *) malloc (sizeof *p);
507                 if (!p)
508                         goto cleanup_entry;
509
510                 p->eptr = eptr;
511                 p->line = line;
512                 p->changed = 0;
513
514                 add_one_entry (db, p);
515         }
516
517         free (buf);
518
519         if (ferror (db->fp))
520                 goto cleanup_errno;
521
522         if (db->ops->open_hook && !db->ops->open_hook ())
523                 goto cleanup_errno;
524
525         db->isopen = 1;
526         return 1;
527
528       cleanup_entry:
529         if (eptr)
530                 db->ops->free (eptr);
531       cleanup_line:
532         free (line);
533       cleanup_buf:
534         free (buf);
535       cleanup_ENOMEM:
536         errno = ENOMEM;
537       cleanup_errno:
538         saved_errno = errno;
539         free_linked_list (db);
540 #ifdef WITH_SELINUX
541         if (db->scontext != NULL) {
542                 freecon (db->scontext);
543                 db->scontext = NULL;
544         }
545 #endif
546         fclose (db->fp);
547         db->fp = NULL;
548         errno = saved_errno;
549         return 0;
550 }
551
552 /*
553  * Sort given db according to cmp function (usually compares uids)
554  */
555 int
556 commonio_sort (struct commonio_db *db, int (*cmp) (const void *, const void *))
557 {
558         struct commonio_entry **entries, *ptr;
559         int n = 0, i;
560
561         for (ptr = db->head; ptr; ptr = ptr->next)
562                 n++;
563
564         if (n <= 1)
565                 return 0;
566
567         entries = malloc (n * sizeof (struct commonio_entry *));
568         if (entries == NULL)
569                 return -1;
570
571         n = 0;
572         for (ptr = db->head; ptr; ptr = ptr->next)
573                 entries[n++] = ptr;
574         qsort (entries, n, sizeof (struct commonio_entry *), cmp);
575
576         db->head = entries[0];
577         db->tail = entries[--n];
578         db->head->prev = NULL;
579         db->head->next = entries[1];
580         db->tail->prev = entries[n - 1];
581         db->tail->next = NULL;
582
583         for (i = 1; i < n; i++) {
584                 entries[i]->prev = entries[i - 1];
585                 entries[i]->next = entries[i + 1];
586         }
587
588         free (entries);
589         db->changed = 1;
590
591         return 0;
592 }
593
594 /*
595  * Sort entries in db according to order in another.
596  */
597 int commonio_sort_wrt (struct commonio_db *shadow, struct commonio_db *passwd)
598 {
599         struct commonio_entry *head = NULL, *pw_ptr, *spw_ptr;
600         const char *name;
601
602         if (!shadow || !shadow->head)
603                 return 0;
604
605         for (pw_ptr = passwd->head; pw_ptr; pw_ptr = pw_ptr->next) {
606                 if (pw_ptr->eptr == NULL)
607                         continue;
608                 name = passwd->ops->getname (pw_ptr->eptr);
609                 for (spw_ptr = shadow->head; spw_ptr; spw_ptr = spw_ptr->next)
610                         if (strcmp (name, shadow->ops->getname (spw_ptr->eptr))
611                             == 0)
612                                 break;
613                 if (spw_ptr == NULL)
614                         continue;
615                 commonio_del_entry (shadow, spw_ptr);
616                 spw_ptr->next = head;
617                 head = spw_ptr;
618         }
619
620         for (spw_ptr = head; spw_ptr; spw_ptr = head) {
621                 head = head->next;
622
623                 if (shadow->head)
624                         shadow->head->prev = spw_ptr;
625                 spw_ptr->next = shadow->head;
626                 shadow->head = spw_ptr;
627         }
628
629         shadow->head->prev = NULL;
630         shadow->changed = 1;
631
632         return 0;
633 }
634
635 /*
636  * write_all - Write the database to its file.
637  *
638  * It returns 0 if all the entries could be writen correctly.
639  */
640 static int write_all (const struct commonio_db *db)
641 {
642         const struct commonio_entry *p;
643         void *eptr;
644
645         for (p = db->head; p; p = p->next) {
646                 if (p->changed) {
647                         eptr = p->eptr;
648                         if (db->ops->put (eptr, db->fp))
649                                 return -1;
650                 } else if (p->line) {
651                         if (db->ops->fputs (p->line, db->fp) == EOF)
652                                 return -1;
653                         if (putc ('\n', db->fp) == EOF)
654                                 return -1;
655                 }
656         }
657         return 0;
658 }
659
660
661 int commonio_close (struct commonio_db *db)
662 {
663         char buf[1024];
664         int errors = 0;
665         struct stat sb;
666
667         if (!db->isopen) {
668                 errno = EINVAL;
669                 return 0;
670         }
671         db->isopen = 0;
672
673         if (!db->changed || db->readonly) {
674                 fclose (db->fp);
675                 db->fp = NULL;
676                 goto success;
677         }
678
679         if (db->ops->close_hook && !db->ops->close_hook ())
680                 goto fail;
681
682         memzero (&sb, sizeof sb);
683         if (db->fp) {
684                 if (fstat (fileno (db->fp), &sb)) {
685                         fclose (db->fp);
686                         db->fp = NULL;
687                         goto fail;
688                 }
689 #ifdef WITH_SELINUX
690                 if (db->scontext != NULL) {
691                         if (getfscreatecon (&old_context) < 0) {
692                                 errors++;
693                                 goto fail;
694                         }
695                         if (setfscreatecon (db->scontext) < 0) {
696                                 errors++;
697                                 goto fail;
698                         }
699                 }
700 #endif
701                 /*
702                  * Create backup file.
703                  */
704                 snprintf (buf, sizeof buf, "%s-", db->filename);
705
706                 if (create_backup (buf, db->fp))
707                         errors++;
708
709                 if (fclose (db->fp))
710                         errors++;
711
712                 if (errors) {
713                         db->fp = NULL;
714                         goto fail;
715                 }
716         } else {
717                 /*
718                  * Default permissions for new [g]shadow files.
719                  * (passwd and group always exist...)
720                  */
721                 sb.st_mode = 0400;
722                 sb.st_uid = 0;
723                 sb.st_gid = 0;
724         }
725
726         snprintf (buf, sizeof buf, "%s+", db->filename);
727
728         db->fp = fopen_set_perms (buf, "w", &sb);
729         if (!db->fp)
730                 goto fail;
731
732         if (write_all (db))
733                 errors++;
734
735         if (fflush (db->fp))
736                 errors++;
737 #ifdef HAVE_FSYNC
738         if (fsync (fileno (db->fp)))
739                 errors++;
740 #else
741         sync ();
742 #endif
743         if (fclose (db->fp))
744                 errors++;
745
746         db->fp = NULL;
747
748         if (errors) {
749                 unlink (buf);
750                 goto fail;
751         }
752
753         if (lrename (buf, db->filename))
754                 goto fail;
755
756         nscd_need_reload = 1;
757         goto success;
758       fail:
759         errors++;
760       success:
761
762 #ifdef WITH_SELINUX
763         if (db->scontext != NULL) {
764                 if (setfscreatecon (old_context) < 0) {
765                         errors++;
766                 }
767                 if (old_context != NULL) {
768                         freecon (old_context);
769                         old_context = NULL;
770                 }
771                 freecon (db->scontext);
772                 db->scontext = NULL;
773         }
774 #endif
775         free_linked_list (db);
776         return errors == 0;
777 }
778
779 static struct commonio_entry *next_entry_by_name (struct commonio_db *db,
780                                                   struct commonio_entry *pos,
781                                                   const char *name)
782 {
783         struct commonio_entry *p;
784         void *ep;
785
786         if (NULL == pos)
787                 return NULL;
788
789         for (p = pos; p; p = p->next) {
790                 ep = p->eptr;
791                 if (ep && strcmp (db->ops->getname (ep), name) == 0)
792                         break;
793         }
794         return p;
795 }
796
797 static struct commonio_entry *find_entry_by_name (struct commonio_db *db,
798                                                   const char *name)
799 {
800         return next_entry_by_name(db, db->head, name);
801 }
802
803
804 int commonio_update (struct commonio_db *db, const void *eptr)
805 {
806         struct commonio_entry *p;
807         void *nentry;
808
809         if (!db->isopen || db->readonly) {
810                 errno = EINVAL;
811                 return 0;
812         }
813         if (!(nentry = db->ops->dup (eptr))) {
814                 errno = ENOMEM;
815                 return 0;
816         }
817         p = find_entry_by_name (db, db->ops->getname (eptr));
818         if (p) {
819                 if (next_entry_by_name (db, p->next, db->ops->getname (eptr)))
820                 {
821                         fprintf (stderr, _("Multiple entries named '%s' in %s. Please fix this with pwck or grpck.\n"), db->ops->getname (eptr), db->filename);
822                         return 0;
823                 }
824                 db->ops->free (p->eptr);
825                 p->eptr = nentry;
826                 p->changed = 1;
827                 db->cursor = p;
828
829                 db->changed = 1;
830                 return 1;
831         }
832         /* not found, new entry */
833         p = (struct commonio_entry *) malloc (sizeof *p);
834         if (!p) {
835                 db->ops->free (nentry);
836                 errno = ENOMEM;
837                 return 0;
838         }
839
840         p->eptr = nentry;
841         p->line = NULL;
842         p->changed = 1;
843
844 #if KEEP_NIS_AT_END
845         add_one_entry_nis (db, p);
846 #else
847         add_one_entry (db, p);
848 #endif
849
850         db->changed = 1;
851         return 1;
852 }
853
854
855 void commonio_del_entry (struct commonio_db *db, const struct commonio_entry *p)
856 {
857         if (p == db->cursor)
858                 db->cursor = p->next;
859
860         if (p->prev)
861                 p->prev->next = p->next;
862         else
863                 db->head = p->next;
864
865         if (p->next)
866                 p->next->prev = p->prev;
867         else
868                 db->tail = p->prev;
869
870         db->changed = 1;
871 }
872
873 /*
874  * commonio_remove - Remove the entry of the given name from the database.
875  */
876 int commonio_remove (struct commonio_db *db, const char *name)
877 {
878         struct commonio_entry *p;
879
880         if (!db->isopen || db->readonly) {
881                 errno = EINVAL;
882                 return 0;
883         }
884         p = find_entry_by_name (db, name);
885         if (!p) {
886                 errno = ENOENT;
887                 return 0;
888         }
889         if (next_entry_by_name (db, p->next, name)) {
890                 fprintf (stderr, _("Multiple entries named '%s' in %s. Please fix this with pwck or grpck.\n"), name, db->filename);
891                 return 0;
892         }
893
894         commonio_del_entry (db, p);
895
896         if (p->line)
897                 free (p->line);
898
899         if (p->eptr)
900                 db->ops->free (p->eptr);
901
902         return 1;
903 }
904
905 /*
906  * commonio_locate - Find the first entry with the specified name in
907  *                   the database.
908  *
909  *      If found, it returns the entry and set the cursor of the database to
910  *      that entry.
911  *
912  *      Otherwise, it returns NULL.
913  */
914 const void *commonio_locate (struct commonio_db *db, const char *name)
915 {
916         struct commonio_entry *p;
917
918         if (!db->isopen) {
919                 errno = EINVAL;
920                 return NULL;
921         }
922         p = find_entry_by_name (db, name);
923         if (!p) {
924                 errno = ENOENT;
925                 return NULL;
926         }
927         db->cursor = p;
928         return p->eptr;
929 }
930
931 /*
932  * commonio_rewind - Restore the database cursor to the first entry.
933  *
934  * It returns 0 on error, 1 on success.
935  */
936 int commonio_rewind (struct commonio_db *db)
937 {
938         if (!db->isopen) {
939                 errno = EINVAL;
940                 return 0;
941         }
942         db->cursor = NULL;
943         return 1;
944 }
945
946 /*
947  * commonio_next - Return the next entry of the specified database
948  *
949  * It returns the next entry, or NULL if no other entries could be found.
950  */
951 const void *commonio_next (struct commonio_db *db)
952 {
953         void *eptr;
954
955         if (!db->isopen) {
956                 errno = EINVAL;
957                 return 0;
958         }
959         if (db->cursor == NULL)
960                 db->cursor = db->head;
961         else
962                 db->cursor = db->cursor->next;
963
964         while (db->cursor) {
965                 eptr = db->cursor->eptr;
966                 if (eptr)
967                         return eptr;
968
969                 db->cursor = db->cursor->next;
970         }
971         return NULL;
972 }