(root)/
man-db-2.12.0/
libdb/
db_btree.c
       1  /*
       2   * db_btree.c: low level btree interface routines for man.
       3   *
       4   * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.)
       5   * Copyright (C) 2002 Colin Watson.
       6   *
       7   * This library is free software; you can redistribute it and/or
       8   * modify it under the terms of the GNU Library General Public
       9   * License as published by the Free Software Foundation; either
      10   * version 2 of the License, or (at your option) any later version.
      11   *
      12   * This library is distributed in the hope that it will be useful,
      13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15   * Library General Public License for more details.
      16   *
      17   * You should have received a copy of the GNU Library General Public
      18   * License along with this library; if not, write to the Free Software
      19   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      20   *
      21   * Mon Aug  8 20:35:30 BST 1994  Wilf. (G.Wilford@ee.surrey.ac.uk)
      22   */
      23  
      24  #ifdef HAVE_CONFIG_H
      25  #  include "config.h"
      26  #endif /* HAVE_CONFIG_H */
      27  
      28  /* below this line are routines only useful for the BTREE interface */
      29  #ifdef BTREE
      30  
      31  #include <stdbool.h>
      32  #include <stdlib.h>
      33  #include <stdio.h>
      34  #include <errno.h>
      35  #include <string.h>
      36  #include <fcntl.h>
      37  #include <unistd.h>
      38  
      39  #include <sys/file.h> /* for flock() */
      40  #include <sys/types.h> /* for open() */
      41  #include <sys/stat.h>
      42  
      43  #include "gl_hash_set.h"
      44  #include "gl_xset.h"
      45  #include "stat-time.h"
      46  #include "timespec.h"
      47  #include "xalloc.h"
      48  #include "xstrndup.h"
      49  
      50  #include "manconfig.h"
      51  
      52  #include "debug.h"
      53  #include "error.h"
      54  #include "glcontainers.h"
      55  
      56  #include "mydbm.h"
      57  #include "db_storage.h"
      58  
      59  gl_set_t loop_check;
      60  
      61  /* the Berkeley database libraries do nothing to arbitrate between concurrent
      62     database accesses, so we do a simple flock(). If the db is opened in
      63     anything but O_RDONLY mode, an exclusive lock is enabled. Otherwise, the
      64     lock is shared. A file cannot have both locks at once, and the non
      65     blocking method is used ": Try again". This adopts GNU dbm's approach. */
      66  
      67  /* release the lock and close the database */
      68  void man_btree_free (man_btree_wrapper wrap)
      69  {
      70  	if (!wrap)
      71  		return;
      72  
      73  	free (wrap->name);
      74  	if (wrap->file) {
      75  		(void) flock ((wrap->file->fd) (wrap->file), LOCK_UN);
      76  		(wrap->file->close) (wrap->file);
      77  	}
      78  	free (wrap->mtime);
      79  	free (wrap);
      80  }
      81  
      82  man_btree_wrapper man_btree_new (const char *name)
      83  {
      84  	man_btree_wrapper wrap;
      85  
      86  	wrap = xmalloc (sizeof *wrap);
      87  	wrap->name = xstrdup (name);
      88  	wrap->file = NULL;
      89  	wrap->mtime = NULL;
      90  
      91  	return wrap;
      92  }
      93  
      94  /* open a btree type database, with file locking. */
      95  bool man_btree_open (man_btree_wrapper wrap, int flags, int mode)
      96  {
      97  	BTREEINFO b;
      98  	int lock_op;
      99  	int lock_failed;
     100  
     101  	b.flags = 0;		/* do not allow any duplicate keys */
     102  
     103  	b.cachesize = 0;	/* default size */
     104  	b.maxkeypage = 0;	/* default */
     105  	b.minkeypage = 0;	/* default */
     106  	b.psize = 0;		/* default page size (2048?) */
     107  	b.compare = NULL;	/* builtin compare() function */
     108  	b.prefix = NULL;	/* builtin function */
     109  	b.lorder = 0;		/* byte order of host */
     110  
     111  	if (flags & ~O_RDONLY) {
     112  		/* flags includes O_RDWR or O_WRONLY, need an exclusive lock */
     113  		lock_op = LOCK_EX | LOCK_NB;
     114  	} else {
     115  		lock_op = LOCK_SH | LOCK_NB;
     116  	}
     117  
     118  	if (!(flags & O_CREAT)) {
     119  		/* Berkeley DB thinks that a zero-length file means that
     120  		 * somebody else is writing it, and sleeps for a few
     121  		 * seconds to give the writer a chance. All very well, but
     122  		 * the common case is that the database is just zero-length
     123  		 * because mandb was interrupted or ran out of disk space or
     124  		 * something like that - so we check for this case by hand
     125  		 * and ignore the database if it's zero-length.
     126  		 */
     127  		struct stat iszero;
     128  		if (stat (wrap->name, &iszero) < 0)
     129  			return false;
     130  		if (iszero.st_size == 0) {
     131  			errno = EINVAL;
     132  			return false;
     133  		}
     134  	}
     135  
     136  	if (flags & O_TRUNC) {
     137  		/* opening the db is destructive, need to lock first */
     138  		int fd;
     139  
     140  		wrap->file = NULL;
     141  		lock_failed = 1;
     142  		fd = open (wrap->name, flags & ~O_TRUNC, mode);
     143  		if (fd != -1) {
     144  			if (!(lock_failed = flock (fd, lock_op)))
     145  				wrap->file = dbopen (wrap->name, flags, mode,
     146  						     DB_BTREE, &b);
     147  			close (fd);
     148  		}
     149  	} else {
     150  		wrap->file = dbopen (wrap->name, flags, mode, DB_BTREE, &b);
     151  		if (wrap->file)
     152  			lock_failed = flock ((wrap->file->fd) (wrap->file),
     153  					     lock_op);
     154  	}
     155  
     156  	if (!wrap->file)
     157  		return false;
     158  
     159  	if (lock_failed) {
     160  		gripe_lock (wrap->name);
     161  		(wrap->file->close) (wrap->file);
     162  		return false;
     163  	}
     164  
     165  	return true;
     166  }
     167  
     168  /* do a replace when we have the duplicate flag set on the database -
     169     we must do a del and insert, as a direct insert will not wipe out the
     170     old entry */
     171  int man_btree_replace (man_btree_wrapper wrap, datum key, datum cont)
     172  {
     173  	return (wrap->file->put) (wrap->file, (DBT *) &key, (DBT *) &cont, 0);
     174  }
     175  
     176  int man_btree_insert (man_btree_wrapper wrap, datum key, datum cont)
     177  {
     178  	return (wrap->file->put) (wrap->file, (DBT *) &key, (DBT *) &cont,
     179  				  R_NOOVERWRITE);
     180  }
     181  
     182  /* generic fetch routine for the btree database */
     183  datum man_btree_fetch (man_btree_wrapper wrap, datum key)
     184  {
     185  	datum data;
     186  
     187  	memset (&data, 0, sizeof data);
     188  
     189  	if ((wrap->file->get) (wrap->file, (DBT *) &key, (DBT *) &data, 0)) {
     190  		memset (&data, 0, sizeof data);
     191  		return data;
     192  	}
     193  
     194  	return copy_datum (data);
     195  }
     196  
     197  /* return 1 if the key exists, 0 otherwise */
     198  int man_btree_exists (man_btree_wrapper wrap, datum key)
     199  {
     200  	datum data;
     201  	return ((wrap->file->get) (wrap->file, (DBT *) &key, (DBT *) &data,
     202  				   0) ? 0 : 1);
     203  }
     204  
     205  /* initiate a sequential access */
     206  static datum man_btree_findkey (man_btree_wrapper wrap, u_int flags)
     207  {
     208  	datum key, data;
     209  	char *loop_check_key;
     210  
     211  	memset (&key, 0, sizeof key);
     212  	memset (&data, 0, sizeof data);
     213  
     214  	if (flags == R_FIRST) {
     215  		if (loop_check) {
     216  			gl_set_free (loop_check);
     217  			loop_check = NULL;
     218  		}
     219  	}
     220  	if (!loop_check)
     221  		loop_check = new_string_set (GL_HASH_SET);
     222  
     223  	if (((wrap->file->seq) (wrap->file, (DBT *) &key, (DBT *) &data,
     224  				flags))) {
     225  		memset (&key, 0, sizeof key);
     226  		return key;
     227  	}
     228  
     229  	loop_check_key = xstrndup (MYDBM_DPTR (key), MYDBM_DSIZE (key));
     230  	if (gl_set_search (loop_check, loop_check_key)) {
     231  		/* We've seen this key already, which is broken. Return NULL
     232  		 * so the caller doesn't go round in circles.
     233  		 */
     234  		debug ("Corrupt database! Already seen %*s. "
     235  		       "Attempting to recover ...\n",
     236  		       (int) MYDBM_DSIZE (key), MYDBM_DPTR (key));
     237  		memset (&key, 0, sizeof key);
     238  		free (loop_check_key);
     239  		return key;
     240  	}
     241  
     242  	gl_set_add (loop_check, loop_check_key);
     243  
     244  	return copy_datum (key);
     245  }
     246  
     247  /* return the first key in the db */
     248  datum man_btree_firstkey (man_btree_wrapper wrap)
     249  {
     250  	return man_btree_findkey (wrap, R_FIRST);
     251  }
     252  
     253  /* return the next key in the db. NB. This routine only works if the cursor
     254     has been previously set by man_btree_firstkey() since it was last opened. So
     255     if we close/reopen a db mid search, we have to manually set up the
     256     cursor again. */
     257  datum man_btree_nextkey (man_btree_wrapper wrap)
     258  {
     259  	return man_btree_findkey (wrap, R_NEXT);
     260  }
     261  
     262  /* compound nextkey routine, initialising key and content */
     263  int man_btree_nextkeydata (man_btree_wrapper wrap, datum *key, datum *cont)
     264  {
     265  	int status;
     266  
     267  	if ((status = (wrap->file->seq) (wrap->file, (DBT *) key, (DBT *) cont,
     268  					 R_NEXT)) != 0)
     269  		return status;
     270  
     271  	*key = copy_datum (*key);
     272  	*cont = copy_datum (*cont);
     273  
     274  	return 0;
     275  }
     276  
     277  struct timespec man_btree_get_time (man_btree_wrapper wrap)
     278  {
     279  	struct stat st;
     280  
     281  	if (!wrap->mtime) {
     282  		wrap->mtime = XMALLOC (struct timespec);
     283  		if (fstat ((wrap->file->fd) (wrap->file), &st) < 0) {
     284  			wrap->mtime->tv_sec = -1;
     285  			wrap->mtime->tv_nsec = -1;
     286  		} else
     287  			*wrap->mtime = get_stat_mtime (&st);
     288  	}
     289  
     290  	return *wrap->mtime;
     291  }
     292  
     293  #endif /* BTREE */