Index: stable/10/lib/libc/db/hash/hash.c =================================================================== --- stable/10/lib/libc/db/hash/hash.c (revision 296423) +++ stable/10/lib/libc/db/hash/hash.c (revision 296424) @@ -1,964 +1,968 @@ /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)hash.c 8.9 (Berkeley) 6/16/94"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include #include #ifdef DEBUG #include #endif #include "un-namespace.h" #include #include "hash.h" #include "page.h" #include "extern.h" static int alloc_segs(HTAB *, int); static int flush_meta(HTAB *); static int hash_access(HTAB *, ACTION, DBT *, DBT *); static int hash_close(DB *); static int hash_delete(const DB *, const DBT *, u_int32_t); static int hash_fd(const DB *); static int hash_get(const DB *, const DBT *, DBT *, u_int32_t); static int hash_put(const DB *, DBT *, const DBT *, u_int32_t); static void *hash_realloc(SEGMENT **, int, int); static int hash_seq(const DB *, DBT *, DBT *, u_int32_t); static int hash_sync(const DB *, u_int32_t); static int hdestroy(HTAB *); static HTAB *init_hash(HTAB *, const char *, const HASHINFO *); static int init_htab(HTAB *, int); #if BYTE_ORDER == LITTLE_ENDIAN static void swap_header(HTAB *); static void swap_header_copy(HASHHDR *, HASHHDR *); #endif /* Fast arithmetic, relying on powers of 2, */ #define MOD(x, y) ((x) & ((y) - 1)) #define RETURN_ERROR(ERR, LOC) { save_errno = ERR; goto LOC; } /* Return values */ #define SUCCESS (0) #define ERROR (-1) #define ABNORMAL (1) #ifdef HASH_STATISTICS int hash_accesses, hash_collisions, hash_expansions, hash_overflows; #endif /************************** INTERFACE ROUTINES ***************************/ /* OPEN/CLOSE */ /* ARGSUSED */ DB * __hash_open(const char *file, int flags, int mode, const HASHINFO *info, /* Special directives for create */ int dflags) { HTAB *hashp; struct stat statbuf; DB *dbp; int bpages, hdrsize, new_table, nsegs, save_errno; if ((flags & O_ACCMODE) == O_WRONLY) { errno = EINVAL; return (NULL); } if (!(hashp = (HTAB *)calloc(1, sizeof(HTAB)))) return (NULL); hashp->fp = -1; /* * Even if user wants write only, we need to be able to read * the actual file, so we need to open it read/write. But, the * field in the hashp structure needs to be accurate so that * we can check accesses. */ hashp->flags = flags; if (file) { if ((hashp->fp = _open(file, flags | O_CLOEXEC, mode)) == -1) RETURN_ERROR(errno, error0); new_table = _fstat(hashp->fp, &statbuf) == 0 && statbuf.st_size == 0 && (flags & O_ACCMODE) != O_RDONLY; } else new_table = 1; if (new_table) { if (!(hashp = init_hash(hashp, file, info))) RETURN_ERROR(errno, error1); } else { /* Table already exists */ if (info && info->hash) hashp->hash = info->hash; else hashp->hash = __default_hash; hdrsize = _read(hashp->fp, &hashp->hdr, sizeof(HASHHDR)); #if BYTE_ORDER == LITTLE_ENDIAN swap_header(hashp); #endif if (hdrsize == -1) RETURN_ERROR(errno, error1); if (hdrsize != sizeof(HASHHDR)) RETURN_ERROR(EFTYPE, error1); /* Verify file type, versions and hash function */ if (hashp->MAGIC != HASHMAGIC) RETURN_ERROR(EFTYPE, error1); #define OLDHASHVERSION 1 if (hashp->VERSION != HASHVERSION && hashp->VERSION != OLDHASHVERSION) RETURN_ERROR(EFTYPE, error1); if ((int32_t)hashp->hash(CHARKEY, sizeof(CHARKEY)) != hashp->H_CHARKEY) RETURN_ERROR(EFTYPE, error1); /* * Figure out how many segments we need. Max_Bucket is the * maximum bucket number, so the number of buckets is * max_bucket + 1. */ nsegs = (hashp->MAX_BUCKET + 1 + hashp->SGSIZE - 1) / hashp->SGSIZE; if (alloc_segs(hashp, nsegs)) /* * If alloc_segs fails, table will have been destroyed * and errno will have been set. */ return (NULL); /* Read in bitmaps */ bpages = (hashp->SPARES[hashp->OVFL_POINT] + (hashp->BSIZE << BYTE_SHIFT) - 1) >> (hashp->BSHIFT + BYTE_SHIFT); hashp->nmaps = bpages; (void)memset(&hashp->mapp[0], 0, bpages * sizeof(u_int32_t *)); } /* Initialize Buffer Manager */ if (info && info->cachesize) __buf_init(hashp, info->cachesize); else __buf_init(hashp, DEF_BUFSIZE); hashp->new_file = new_table; hashp->save_file = file && (hashp->flags & O_RDWR); hashp->cbucket = -1; if (!(dbp = (DB *)malloc(sizeof(DB)))) { save_errno = errno; hdestroy(hashp); errno = save_errno; return (NULL); } dbp->internal = hashp; dbp->close = hash_close; dbp->del = hash_delete; dbp->fd = hash_fd; dbp->get = hash_get; dbp->put = hash_put; dbp->seq = hash_seq; dbp->sync = hash_sync; dbp->type = DB_HASH; #ifdef DEBUG (void)fprintf(stderr, "%s\n%s%p\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%x\n%s%x\n%s%d\n%s%d\n", "init_htab:", "TABLE POINTER ", hashp, "BUCKET SIZE ", hashp->BSIZE, "BUCKET SHIFT ", hashp->BSHIFT, "DIRECTORY SIZE ", hashp->DSIZE, "SEGMENT SIZE ", hashp->SGSIZE, "SEGMENT SHIFT ", hashp->SSHIFT, "FILL FACTOR ", hashp->FFACTOR, "MAX BUCKET ", hashp->MAX_BUCKET, "OVFL POINT ", hashp->OVFL_POINT, "LAST FREED ", hashp->LAST_FREED, "HIGH MASK ", hashp->HIGH_MASK, "LOW MASK ", hashp->LOW_MASK, "NSEGS ", hashp->nsegs, "NKEYS ", hashp->NKEYS); #endif #ifdef HASH_STATISTICS hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0; #endif return (dbp); error1: if (hashp != NULL) (void)_close(hashp->fp); error0: free(hashp); errno = save_errno; return (NULL); } static int hash_close(DB *dbp) { HTAB *hashp; int retval; if (!dbp) return (ERROR); hashp = (HTAB *)dbp->internal; retval = hdestroy(hashp); free(dbp); return (retval); } static int hash_fd(const DB *dbp) { HTAB *hashp; if (!dbp) return (ERROR); hashp = (HTAB *)dbp->internal; if (hashp->fp == -1) { errno = ENOENT; return (-1); } return (hashp->fp); } /************************** LOCAL CREATION ROUTINES **********************/ static HTAB * init_hash(HTAB *hashp, const char *file, const HASHINFO *info) { struct stat statbuf; int nelem; nelem = 1; hashp->NKEYS = 0; hashp->LORDER = BYTE_ORDER; hashp->BSIZE = DEF_BUCKET_SIZE; hashp->BSHIFT = DEF_BUCKET_SHIFT; hashp->SGSIZE = DEF_SEGSIZE; hashp->SSHIFT = DEF_SEGSIZE_SHIFT; hashp->DSIZE = DEF_DIRSIZE; hashp->FFACTOR = DEF_FFACTOR; hashp->hash = __default_hash; memset(hashp->SPARES, 0, sizeof(hashp->SPARES)); memset(hashp->BITMAPS, 0, sizeof (hashp->BITMAPS)); /* Fix bucket size to be optimal for file system */ if (file != NULL) { if (stat(file, &statbuf)) return (NULL); hashp->BSIZE = statbuf.st_blksize; if (hashp->BSIZE > MAX_BSIZE) hashp->BSIZE = MAX_BSIZE; hashp->BSHIFT = __log2(hashp->BSIZE); } if (info) { if (info->bsize) { /* Round pagesize up to power of 2 */ hashp->BSHIFT = __log2(info->bsize); hashp->BSIZE = 1 << hashp->BSHIFT; if (hashp->BSIZE > MAX_BSIZE) { errno = EINVAL; return (NULL); } } if (info->ffactor) hashp->FFACTOR = info->ffactor; if (info->hash) hashp->hash = info->hash; if (info->nelem) nelem = info->nelem; if (info->lorder) { if (info->lorder != BIG_ENDIAN && info->lorder != LITTLE_ENDIAN) { errno = EINVAL; return (NULL); } hashp->LORDER = info->lorder; } } /* init_htab should destroy the table and set errno if it fails */ if (init_htab(hashp, nelem)) return (NULL); else return (hashp); } /* * This calls alloc_segs which may run out of memory. Alloc_segs will destroy * the table and set errno, so we just pass the error information along. * * Returns 0 on No Error */ static int init_htab(HTAB *hashp, int nelem) { int nbuckets, nsegs, l2; /* * Divide number of elements by the fill factor and determine a * desired number of buckets. Allocate space for the next greater * power of two number of buckets. */ nelem = (nelem - 1) / hashp->FFACTOR + 1; l2 = __log2(MAX(nelem, 2)); nbuckets = 1 << l2; hashp->SPARES[l2] = l2 + 1; hashp->SPARES[l2 + 1] = l2 + 1; hashp->OVFL_POINT = l2; hashp->LAST_FREED = 2; /* First bitmap page is at: splitpoint l2 page offset 1 */ if (__ibitmap(hashp, OADDR_OF(l2, 1), l2 + 1, 0)) return (-1); hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1; hashp->HIGH_MASK = (nbuckets << 1) - 1; hashp->HDRPAGES = ((MAX(sizeof(HASHHDR), MINHDRSIZE) - 1) >> hashp->BSHIFT) + 1; nsegs = (nbuckets - 1) / hashp->SGSIZE + 1; nsegs = 1 << __log2(nsegs); if (nsegs > hashp->DSIZE) hashp->DSIZE = nsegs; return (alloc_segs(hashp, nsegs)); } /********************** DESTROY/CLOSE ROUTINES ************************/ /* * Flushes any changes to the file if necessary and destroys the hashp * structure, freeing all allocated space. */ static int hdestroy(HTAB *hashp) { int i, save_errno; save_errno = 0; #ifdef HASH_STATISTICS (void)fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n", hash_accesses, hash_collisions); (void)fprintf(stderr, "hdestroy: expansions %ld\n", hash_expansions); (void)fprintf(stderr, "hdestroy: overflows %ld\n", hash_overflows); (void)fprintf(stderr, "keys %ld maxp %d segmentcount %d\n", hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs); for (i = 0; i < NCACHED; i++) (void)fprintf(stderr, "spares[%d] = %d\n", i, hashp->SPARES[i]); #endif /* * Call on buffer manager to free buffers, and if required, * write them to disk. */ if (__buf_free(hashp, 1, hashp->save_file)) save_errno = errno; if (hashp->dir) { free(*hashp->dir); /* Free initial segments */ /* Free extra segments */ while (hashp->exsegs--) free(hashp->dir[--hashp->nsegs]); free(hashp->dir); } if (flush_meta(hashp) && !save_errno) save_errno = errno; /* Free Bigmaps */ for (i = 0; i < hashp->nmaps; i++) if (hashp->mapp[i]) free(hashp->mapp[i]); if (hashp->tmp_key) free(hashp->tmp_key); if (hashp->tmp_buf) free(hashp->tmp_buf); - if (hashp->fp != -1) + if (hashp->fp != -1) { + (void)_fsync(hashp->fp); (void)_close(hashp->fp); + } free(hashp); if (save_errno) { errno = save_errno; return (ERROR); } return (SUCCESS); } /* * Write modified pages to disk * * Returns: * 0 == OK * -1 ERROR */ static int hash_sync(const DB *dbp, u_int32_t flags) { HTAB *hashp; if (flags != 0) { errno = EINVAL; return (ERROR); } if (!dbp) return (ERROR); hashp = (HTAB *)dbp->internal; if (!hashp->save_file) return (0); if (__buf_free(hashp, 0, 1) || flush_meta(hashp)) + return (ERROR); + if (hashp->fp != -1 && _fsync(hashp->fp) != 0) return (ERROR); hashp->new_file = 0; return (0); } /* * Returns: * 0 == OK * -1 indicates that errno should be set */ static int flush_meta(HTAB *hashp) { HASHHDR *whdrp; #if BYTE_ORDER == LITTLE_ENDIAN HASHHDR whdr; #endif int fp, i, wsize; if (!hashp->save_file) return (0); hashp->MAGIC = HASHMAGIC; hashp->VERSION = HASHVERSION; hashp->H_CHARKEY = hashp->hash(CHARKEY, sizeof(CHARKEY)); fp = hashp->fp; whdrp = &hashp->hdr; #if BYTE_ORDER == LITTLE_ENDIAN whdrp = &whdr; swap_header_copy(&hashp->hdr, whdrp); #endif if ((wsize = pwrite(fp, whdrp, sizeof(HASHHDR), (off_t)0)) == -1) return (-1); else if (wsize != sizeof(HASHHDR)) { errno = EFTYPE; hashp->error = errno; return (-1); } for (i = 0; i < NCACHED; i++) if (hashp->mapp[i]) if (__put_page(hashp, (char *)hashp->mapp[i], hashp->BITMAPS[i], 0, 1)) return (-1); return (0); } /*******************************SEARCH ROUTINES *****************************/ /* * All the access routines return * * Returns: * 0 on SUCCESS * 1 to indicate an external ERROR (i.e. key not found, etc) * -1 to indicate an internal ERROR (i.e. out of memory, etc) */ static int hash_get(const DB *dbp, const DBT *key, DBT *data, u_int32_t flag) { HTAB *hashp; hashp = (HTAB *)dbp->internal; if (flag) { hashp->error = errno = EINVAL; return (ERROR); } return (hash_access(hashp, HASH_GET, (DBT *)key, data)); } static int hash_put(const DB *dbp, DBT *key, const DBT *data, u_int32_t flag) { HTAB *hashp; hashp = (HTAB *)dbp->internal; if (flag && flag != R_NOOVERWRITE) { hashp->error = errno = EINVAL; return (ERROR); } if ((hashp->flags & O_ACCMODE) == O_RDONLY) { hashp->error = errno = EPERM; return (ERROR); } return (hash_access(hashp, flag == R_NOOVERWRITE ? HASH_PUTNEW : HASH_PUT, (DBT *)key, (DBT *)data)); } static int hash_delete(const DB *dbp, const DBT *key, u_int32_t flag) /* Ignored */ { HTAB *hashp; hashp = (HTAB *)dbp->internal; if (flag && flag != R_CURSOR) { hashp->error = errno = EINVAL; return (ERROR); } if ((hashp->flags & O_ACCMODE) == O_RDONLY) { hashp->error = errno = EPERM; return (ERROR); } return (hash_access(hashp, HASH_DELETE, (DBT *)key, NULL)); } /* * Assume that hashp has been set in wrapper routine. */ static int hash_access(HTAB *hashp, ACTION action, DBT *key, DBT *val) { BUFHEAD *rbufp; BUFHEAD *bufp, *save_bufp; u_int16_t *bp; int n, ndx, off, size; char *kp; u_int16_t pageno; #ifdef HASH_STATISTICS hash_accesses++; #endif off = hashp->BSIZE; size = key->size; kp = (char *)key->data; rbufp = __get_buf(hashp, __call_hash(hashp, kp, size), NULL, 0); if (!rbufp) return (ERROR); save_bufp = rbufp; /* Pin the bucket chain */ rbufp->flags |= BUF_PIN; for (bp = (u_int16_t *)rbufp->page, n = *bp++, ndx = 1; ndx < n;) if (bp[1] >= REAL_KEY) { /* Real key/data pair */ if (size == off - *bp && memcmp(kp, rbufp->page + *bp, size) == 0) goto found; off = bp[1]; #ifdef HASH_STATISTICS hash_collisions++; #endif bp += 2; ndx += 2; } else if (bp[1] == OVFLPAGE) { rbufp = __get_buf(hashp, *bp, rbufp, 0); if (!rbufp) { save_bufp->flags &= ~BUF_PIN; return (ERROR); } /* FOR LOOP INIT */ bp = (u_int16_t *)rbufp->page; n = *bp++; ndx = 1; off = hashp->BSIZE; } else if (bp[1] < REAL_KEY) { if ((ndx = __find_bigpair(hashp, rbufp, ndx, kp, size)) > 0) goto found; if (ndx == -2) { bufp = rbufp; if (!(pageno = __find_last_page(hashp, &bufp))) { ndx = 0; rbufp = bufp; break; /* FOR */ } rbufp = __get_buf(hashp, pageno, bufp, 0); if (!rbufp) { save_bufp->flags &= ~BUF_PIN; return (ERROR); } /* FOR LOOP INIT */ bp = (u_int16_t *)rbufp->page; n = *bp++; ndx = 1; off = hashp->BSIZE; } else { save_bufp->flags &= ~BUF_PIN; return (ERROR); } } /* Not found */ switch (action) { case HASH_PUT: case HASH_PUTNEW: if (__addel(hashp, rbufp, key, val)) { save_bufp->flags &= ~BUF_PIN; return (ERROR); } else { save_bufp->flags &= ~BUF_PIN; return (SUCCESS); } case HASH_GET: case HASH_DELETE: default: save_bufp->flags &= ~BUF_PIN; return (ABNORMAL); } found: switch (action) { case HASH_PUTNEW: save_bufp->flags &= ~BUF_PIN; return (ABNORMAL); case HASH_GET: bp = (u_int16_t *)rbufp->page; if (bp[ndx + 1] < REAL_KEY) { if (__big_return(hashp, rbufp, ndx, val, 0)) return (ERROR); } else { val->data = (u_char *)rbufp->page + (int)bp[ndx + 1]; val->size = bp[ndx] - bp[ndx + 1]; } break; case HASH_PUT: if ((__delpair(hashp, rbufp, ndx)) || (__addel(hashp, rbufp, key, val))) { save_bufp->flags &= ~BUF_PIN; return (ERROR); } break; case HASH_DELETE: if (__delpair(hashp, rbufp, ndx)) return (ERROR); break; default: abort(); } save_bufp->flags &= ~BUF_PIN; return (SUCCESS); } static int hash_seq(const DB *dbp, DBT *key, DBT *data, u_int32_t flag) { u_int32_t bucket; BUFHEAD *bufp; HTAB *hashp; u_int16_t *bp, ndx; hashp = (HTAB *)dbp->internal; if (flag && flag != R_FIRST && flag != R_NEXT) { hashp->error = errno = EINVAL; return (ERROR); } #ifdef HASH_STATISTICS hash_accesses++; #endif if ((hashp->cbucket < 0) || (flag == R_FIRST)) { hashp->cbucket = 0; hashp->cndx = 1; hashp->cpage = NULL; } next_bucket: for (bp = NULL; !bp || !bp[0]; ) { if (!(bufp = hashp->cpage)) { for (bucket = hashp->cbucket; bucket <= hashp->MAX_BUCKET; bucket++, hashp->cndx = 1) { bufp = __get_buf(hashp, bucket, NULL, 0); if (!bufp) return (ERROR); hashp->cpage = bufp; bp = (u_int16_t *)bufp->page; if (bp[0]) break; } hashp->cbucket = bucket; if ((u_int32_t)hashp->cbucket > hashp->MAX_BUCKET) { hashp->cbucket = -1; return (ABNORMAL); } } else { bp = (u_int16_t *)hashp->cpage->page; if (flag == R_NEXT || flag == 0) { hashp->cndx += 2; if (hashp->cndx > bp[0]) { hashp->cpage = NULL; hashp->cbucket++; hashp->cndx = 1; goto next_bucket; } } } #ifdef DEBUG assert(bp); assert(bufp); #endif while (bp[hashp->cndx + 1] == OVFLPAGE) { bufp = hashp->cpage = __get_buf(hashp, bp[hashp->cndx], bufp, 0); if (!bufp) return (ERROR); bp = (u_int16_t *)(bufp->page); hashp->cndx = 1; } if (!bp[0]) { hashp->cpage = NULL; ++hashp->cbucket; } } ndx = hashp->cndx; if (bp[ndx + 1] < REAL_KEY) { if (__big_keydata(hashp, bufp, key, data, 1)) return (ERROR); } else { if (hashp->cpage == 0) return (ERROR); key->data = (u_char *)hashp->cpage->page + bp[ndx]; key->size = (ndx > 1 ? bp[ndx - 1] : hashp->BSIZE) - bp[ndx]; data->data = (u_char *)hashp->cpage->page + bp[ndx + 1]; data->size = bp[ndx] - bp[ndx + 1]; } return (SUCCESS); } /********************************* UTILITIES ************************/ /* * Returns: * 0 ==> OK * -1 ==> Error */ int __expand_table(HTAB *hashp) { u_int32_t old_bucket, new_bucket; int dirsize, new_segnum, spare_ndx; #ifdef HASH_STATISTICS hash_expansions++; #endif new_bucket = ++hashp->MAX_BUCKET; old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK); new_segnum = new_bucket >> hashp->SSHIFT; /* Check if we need a new segment */ if (new_segnum >= hashp->nsegs) { /* Check if we need to expand directory */ if (new_segnum >= hashp->DSIZE) { /* Reallocate directory */ dirsize = hashp->DSIZE * sizeof(SEGMENT *); if (!hash_realloc(&hashp->dir, dirsize, dirsize << 1)) return (-1); hashp->DSIZE = dirsize << 1; } if ((hashp->dir[new_segnum] = (SEGMENT)calloc(hashp->SGSIZE, sizeof(SEGMENT))) == NULL) return (-1); hashp->exsegs++; hashp->nsegs++; } /* * If the split point is increasing (MAX_BUCKET's log base 2 * * increases), we need to copy the current contents of the spare * split bucket to the next bucket. */ spare_ndx = __log2(hashp->MAX_BUCKET + 1); if (spare_ndx > hashp->OVFL_POINT) { hashp->SPARES[spare_ndx] = hashp->SPARES[hashp->OVFL_POINT]; hashp->OVFL_POINT = spare_ndx; } if (new_bucket > hashp->HIGH_MASK) { /* Starting a new doubling */ hashp->LOW_MASK = hashp->HIGH_MASK; hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK; } /* Relocate records to the new bucket */ return (__split_page(hashp, old_bucket, new_bucket)); } /* * If realloc guarantees that the pointer is not destroyed if the realloc * fails, then this routine can go away. */ static void * hash_realloc(SEGMENT **p_ptr, int oldsize, int newsize) { void *p; if ( (p = malloc(newsize)) ) { memmove(p, *p_ptr, oldsize); memset((char *)p + oldsize, 0, newsize - oldsize); free(*p_ptr); *p_ptr = p; } return (p); } u_int32_t __call_hash(HTAB *hashp, char *k, int len) { unsigned int n, bucket; n = hashp->hash(k, len); bucket = n & hashp->HIGH_MASK; if (bucket > hashp->MAX_BUCKET) bucket = bucket & hashp->LOW_MASK; return (bucket); } /* * Allocate segment table. On error, destroy the table and set errno. * * Returns 0 on success */ static int alloc_segs(HTAB *hashp, int nsegs) { int i; SEGMENT store; int save_errno; if ((hashp->dir = (SEGMENT *)calloc(hashp->DSIZE, sizeof(SEGMENT *))) == NULL) { save_errno = errno; (void)hdestroy(hashp); errno = save_errno; return (-1); } hashp->nsegs = nsegs; if (nsegs == 0) return (0); /* Allocate segments */ if ((store = (SEGMENT)calloc(nsegs << hashp->SSHIFT, sizeof(SEGMENT))) == NULL) { save_errno = errno; (void)hdestroy(hashp); errno = save_errno; return (-1); } for (i = 0; i < nsegs; i++) hashp->dir[i] = &store[i << hashp->SSHIFT]; return (0); } #if BYTE_ORDER == LITTLE_ENDIAN /* * Hashp->hdr needs to be byteswapped. */ static void swap_header_copy(HASHHDR *srcp, HASHHDR *destp) { int i; P_32_COPY(srcp->magic, destp->magic); P_32_COPY(srcp->version, destp->version); P_32_COPY(srcp->lorder, destp->lorder); P_32_COPY(srcp->bsize, destp->bsize); P_32_COPY(srcp->bshift, destp->bshift); P_32_COPY(srcp->dsize, destp->dsize); P_32_COPY(srcp->ssize, destp->ssize); P_32_COPY(srcp->sshift, destp->sshift); P_32_COPY(srcp->ovfl_point, destp->ovfl_point); P_32_COPY(srcp->last_freed, destp->last_freed); P_32_COPY(srcp->max_bucket, destp->max_bucket); P_32_COPY(srcp->high_mask, destp->high_mask); P_32_COPY(srcp->low_mask, destp->low_mask); P_32_COPY(srcp->ffactor, destp->ffactor); P_32_COPY(srcp->nkeys, destp->nkeys); P_32_COPY(srcp->hdrpages, destp->hdrpages); P_32_COPY(srcp->h_charkey, destp->h_charkey); for (i = 0; i < NCACHED; i++) { P_32_COPY(srcp->spares[i], destp->spares[i]); P_16_COPY(srcp->bitmaps[i], destp->bitmaps[i]); } } static void swap_header(HTAB *hashp) { HASHHDR *hdrp; int i; hdrp = &hashp->hdr; M_32_SWAP(hdrp->magic); M_32_SWAP(hdrp->version); M_32_SWAP(hdrp->lorder); M_32_SWAP(hdrp->bsize); M_32_SWAP(hdrp->bshift); M_32_SWAP(hdrp->dsize); M_32_SWAP(hdrp->ssize); M_32_SWAP(hdrp->sshift); M_32_SWAP(hdrp->ovfl_point); M_32_SWAP(hdrp->last_freed); M_32_SWAP(hdrp->max_bucket); M_32_SWAP(hdrp->high_mask); M_32_SWAP(hdrp->low_mask); M_32_SWAP(hdrp->ffactor); M_32_SWAP(hdrp->nkeys); M_32_SWAP(hdrp->hdrpages); M_32_SWAP(hdrp->h_charkey); for (i = 0; i < NCACHED; i++) { M_32_SWAP(hdrp->spares[i]); M_16_SWAP(hdrp->bitmaps[i]); } } #endif Index: stable/10/usr.bin/cap_mkdb/cap_mkdb.c =================================================================== --- stable/10/usr.bin/cap_mkdb/cap_mkdb.c (revision 296423) +++ stable/10/usr.bin/cap_mkdb/cap_mkdb.c (revision 296424) @@ -1,268 +1,268 @@ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1992, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif #if 0 #ifndef lint static char sccsid[] = "@(#)cap_mkdb.c 8.2 (Berkeley) 4/27/95"; #endif #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include static void db_build(char **); static void dounlink(void); static void usage(void); static DB *capdbp; static int verbose; static char *capname, buf[8 * 1024]; static HASHINFO openinfo = { 4096, /* bsize */ 0, /* ffactor */ 0, /* nelem */ 0, /* cachesize */ NULL, /* hash() */ 0 /* lorder */ }; /* * Mkcapdb creates a capability hash database for quick retrieval of capability * records. The database contains 2 types of entries: records and references * marked by the first byte in the data. A record entry contains the actual * capability record whereas a reference contains the name (key) under which * the correct record is stored. */ int main(int argc, char *argv[]) { int byteorder, c; capname = NULL; byteorder = 0; while ((c = getopt(argc, argv, "bf:lv")) != -1) { switch(c) { case 'b': case 'l': if (byteorder != 0) usage(); byteorder = c == 'b' ? 4321 : 1234; break; case 'f': capname = optarg; break; case 'v': verbose = 1; break; case '?': default: usage(); } } argc -= optind; argv += optind; if (*argv == NULL) usage(); /* Set byte order. */ openinfo.lorder = byteorder; /* * The database file is the first argument if no name is specified. * Make arrangements to unlink it if exit badly. */ (void)snprintf(buf, sizeof(buf), "%s.db", capname ? capname : *argv); if ((capname = strdup(buf)) == NULL) errx(1, "strdup failed"); - if ((capdbp = dbopen(capname, O_CREAT | O_TRUNC | O_RDWR | O_SYNC, + if ((capdbp = dbopen(capname, O_CREAT | O_TRUNC | O_RDWR, DEFFILEMODE, DB_HASH, &openinfo)) == NULL) err(1, "%s", buf); if (atexit(dounlink)) err(1, "atexit"); db_build(argv); if (capdbp->close(capdbp) < 0) err(1, "%s", capname); capname = NULL; exit(0); } static void dounlink(void) { if (capname != NULL) (void)unlink(capname); } /* * Any changes to these definitions should be made also in the getcap(3) * library routines. */ #define RECOK (char)0 #define TCERR (char)1 #define SHADOW (char)2 /* * Db_build() builds the name and capability databases according to the * details above. */ static void db_build(char **ifiles) { DBT key, data; recno_t reccnt; size_t len, bplen; int st; char *bp, *p, *t; data.data = NULL; key.data = NULL; for (reccnt = 0, bplen = 0; (st = cgetnext(&bp, ifiles)) > 0;) { /* * Allocate enough memory to store record, terminating * NULL and one extra byte. */ len = strlen(bp); if (bplen <= len + 2) { bplen += MAX(256, len + 2); if ((data.data = realloc(data.data, bplen)) == NULL) errx(1, "malloc failed"); } /* Find the end of the name field. */ if ((p = strchr(bp, ':')) == NULL) { warnx("no name field: %.*s", (int)MIN(len, 20), bp); continue; } /* First byte of stored record indicates status. */ switch(st) { case 1: ((char *)(data.data))[0] = RECOK; break; case 2: ((char *)(data.data))[0] = TCERR; warnx("record not tc expanded: %.*s", (int)(p - bp), bp); break; } /* Create the stored record. */ memmove(&((u_char *)(data.data))[1], bp, len + 1); data.size = len + 2; /* Store the record under the name field. */ key.data = bp; key.size = p - bp; switch(capdbp->put(capdbp, &key, &data, R_NOOVERWRITE)) { case -1: err(1, "put"); /* NOTREACHED */ case 1: warnx("ignored duplicate: %.*s", (int)key.size, (char *)key.data); continue; } ++reccnt; /* If only one name, ignore the rest. */ *p = '\0'; if (strchr(bp, '|') == NULL) continue; *p = ':'; /* The rest of the names reference the entire name. */ ((char *)(data.data))[0] = SHADOW; memmove(&((u_char *)(data.data))[1], key.data, key.size); data.size = key.size + 1; /* Store references for other names. */ for (p = t = bp;; ++p) { if (p > t && (*p == ':' || *p == '|')) { key.size = p - t; key.data = t; switch(capdbp->put(capdbp, &key, &data, R_NOOVERWRITE)) { case -1: err(1, "put"); /* NOTREACHED */ case 1: warnx("ignored duplicate: %.*s", (int)key.size, (char *)key.data); } t = p + 1; } if (*p == ':') break; } } switch(st) { case -1: err(1, "file argument"); /* NOTREACHED */ case -2: errx(1, "potential reference loop detected"); /* NOTREACHED */ } if (verbose) (void)printf("cap_mkdb: %d capability records\n", reccnt); } static void usage(void) { (void)fprintf(stderr, "usage: cap_mkdb [-b | -l] [-v] [-f outfile] file ...\n"); exit(1); } Index: stable/10/usr.sbin/pwd_mkdb/pwd_mkdb.c =================================================================== --- stable/10/usr.sbin/pwd_mkdb/pwd_mkdb.c (revision 296423) +++ stable/10/usr.sbin/pwd_mkdb/pwd_mkdb.c (revision 296424) @@ -1,777 +1,777 @@ /*- * Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if 0 #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1991, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)pwd_mkdb.c 8.5 (Berkeley) 4/20/94"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pw_scan.h" #define INSECURE 1 #define SECURE 2 #define PERM_INSECURE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) #define PERM_SECURE (S_IRUSR|S_IWUSR) #define LEGACY_VERSION(x) _PW_VERSIONED(x, 3) #define CURRENT_VERSION(x) _PW_VERSIONED(x, 4) static HASHINFO openinfo = { 4096, /* bsize */ 32, /* ffactor */ 256, /* nelem */ 2048 * 1024, /* cachesize */ NULL, /* hash() */ BYTE_ORDER /* lorder */ }; static enum state { FILE_INSECURE, FILE_SECURE, FILE_ORIG } clean; static struct passwd pwd; /* password structure */ static char *pname; /* password file name */ static char prefix[MAXPATHLEN]; static int is_comment; /* flag for comments */ static char line[LINE_MAX]; void cleanup(void); void error(const char *); void cp(char *, char *, mode_t mode); void mv(char *, char *); int scan(FILE *, struct passwd *); static void usage(void); int main(int argc, char *argv[]) { static char verskey[] = _PWD_VERSION_KEY; char version = _PWD_CURRENT_VERSION; DB *dp, *sdp, *pw_db; DBT data, sdata, key; FILE *fp, *oldfp; sigset_t set; int ch, cnt, ypcnt, makeold, tfd, yp_enabled = 0; unsigned int len; uint32_t store; const char *t; char *p; char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024]; char sbuf[MAX(MAXPATHLEN, LINE_MAX * 2)]; char buf2[MAXPATHLEN]; char sbuf2[MAXPATHLEN]; char *username; u_int method, methoduid; int Cflag, dflag, iflag; int nblock = 0; iflag = dflag = Cflag = 0; strcpy(prefix, _PATH_PWD); makeold = 0; username = NULL; oldfp = NULL; while ((ch = getopt(argc, argv, "BCLNd:ips:u:v")) != -1) switch(ch) { case 'B': /* big-endian output */ openinfo.lorder = BIG_ENDIAN; break; case 'C': /* verify only */ Cflag = 1; break; case 'L': /* little-endian output */ openinfo.lorder = LITTLE_ENDIAN; break; case 'N': /* do not wait for lock */ nblock = LOCK_NB; /* will fail if locked */ break; case 'd': dflag++; strlcpy(prefix, optarg, sizeof(prefix)); break; case 'i': iflag++; break; case 'p': /* create V7 "file.orig" */ makeold = 1; break; case 's': /* change default cachesize */ openinfo.cachesize = atoi(optarg) * 1024 * 1024; break; case 'u': /* only update this record */ username = optarg; break; case 'v': /* backward compatible */ break; default: usage(); } argc -= optind; argv += optind; if (argc != 1 || (username && (*username == '+' || *username == '-'))) usage(); /* * This could be changed to allow the user to interrupt. * Probably not worth the effort. */ sigemptyset(&set); sigaddset(&set, SIGTSTP); sigaddset(&set, SIGHUP); sigaddset(&set, SIGINT); sigaddset(&set, SIGQUIT); sigaddset(&set, SIGTERM); (void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL); /* We don't care what the user wants. */ (void)umask(0); pname = *argv; /* * Open and lock the original password file. We have to check * the hardlink count after we get the lock to handle any potential * unlink/rename race. * * This lock is necessary when someone runs pwd_mkdb manually, directly * on master.passwd, to handle the case where a user might try to * change his password while pwd_mkdb is running. */ for (;;) { struct stat st; if (!(fp = fopen(pname, "r"))) error(pname); if (flock(fileno(fp), LOCK_EX|nblock) < 0 && !(dflag && iflag)) error("flock"); if (fstat(fileno(fp), &st) < 0) error(pname); if (st.st_nlink != 0) break; fclose(fp); fp = NULL; } /* check only if password database is valid */ if (Cflag) { while (scan(fp, &pwd)) if (!is_comment && strlen(pwd.pw_name) >= MAXLOGNAME) { warnx("%s: username too long", pwd.pw_name); exit(1); } exit(0); } /* Open the temporary insecure password database. */ (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB); (void)snprintf(sbuf, sizeof(sbuf), "%s/%s.tmp", prefix, _SMP_DB); if (username) { int use_version; (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB); (void)snprintf(sbuf2, sizeof(sbuf2), "%s/%s", prefix, _SMP_DB); clean = FILE_INSECURE; cp(buf2, buf, PERM_INSECURE); dp = dbopen(buf, - O_RDWR|O_EXCL|O_SYNC, PERM_INSECURE, DB_HASH, &openinfo); + O_RDWR|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo); if (dp == NULL) error(buf); clean = FILE_SECURE; cp(sbuf2, sbuf, PERM_SECURE); sdp = dbopen(sbuf, - O_RDWR|O_EXCL|O_SYNC, PERM_SECURE, DB_HASH, &openinfo); + O_RDWR|O_EXCL, PERM_SECURE, DB_HASH, &openinfo); if (sdp == NULL) error(sbuf); /* * Do some trouble to check if we should store this users * uid. Don't use getpwnam/getpwuid as that interferes * with NIS. */ pw_db = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL); if (!pw_db) error(_MP_DB); key.data = verskey; key.size = sizeof(verskey)-1; if ((pw_db->get)(pw_db, &key, &data, 0) == 0) use_version = *(unsigned char *)data.data; else use_version = 3; buf[0] = _PW_VERSIONED(_PW_KEYBYNAME, use_version); len = strlen(username); /* Only check that username fits in buffer */ memmove(buf + 1, username, MIN(len, sizeof(buf) - 1)); key.data = (u_char *)buf; key.size = len + 1; if ((pw_db->get)(pw_db, &key, &data, 0) == 0) { p = (char *)data.data; /* jump over pw_name and pw_passwd, to get to pw_uid */ while (*p++) ; while (*p++) ; buf[0] = _PW_VERSIONED(_PW_KEYBYUID, use_version); memmove(buf + 1, p, sizeof(store)); key.data = (u_char *)buf; key.size = sizeof(store) + 1; if ((pw_db->get)(pw_db, &key, &data, 0) == 0) { /* First field of data.data holds pw_pwname */ if (!strcmp(data.data, username)) methoduid = 0; else methoduid = R_NOOVERWRITE; } else { methoduid = R_NOOVERWRITE; } } else { methoduid = R_NOOVERWRITE; } if ((pw_db->close)(pw_db)) error("close pw_db"); method = 0; } else { dp = dbopen(buf, - O_RDWR|O_CREAT|O_EXCL|O_SYNC, PERM_INSECURE, DB_HASH, &openinfo); + O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo); if (dp == NULL) error(buf); clean = FILE_INSECURE; sdp = dbopen(sbuf, - O_RDWR|O_CREAT|O_EXCL|O_SYNC, PERM_SECURE, DB_HASH, &openinfo); + O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo); if (sdp == NULL) error(sbuf); clean = FILE_SECURE; method = R_NOOVERWRITE; methoduid = R_NOOVERWRITE; } /* * Open file for old password file. Minor trickiness -- don't want to * chance the file already existing, since someone (stupidly) might * still be using this for permission checking. So, open it first and * fdopen the resulting fd. The resulting file should be readable by * everyone. */ if (makeold) { (void)snprintf(buf, sizeof(buf), "%s.orig", pname); if ((tfd = open(buf, O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0) error(buf); if ((oldfp = fdopen(tfd, "w")) == NULL) error(buf); clean = FILE_ORIG; } /* * The databases actually contain three copies of the original data. * Each password file entry is converted into a rough approximation * of a ``struct passwd'', with the strings placed inline. This * object is then stored as the data for three separate keys. The * first key * is the pw_name field prepended by the _PW_KEYBYNAME * character. The second key is the pw_uid field prepended by the * _PW_KEYBYUID character. The third key is the line number in the * original file prepended by the _PW_KEYBYNUM character. (The special * characters are prepended to ensure that the keys do not collide.) */ /* In order to transition this file into a machine-independent * form, we have to change the format of entries. However, since * older binaries will still expect the old MD format entries, we * create those as usual and use versioned tags for the new entries. */ if (username == NULL) { /* Do not add the VERSION tag when updating a single * user. When operating on `old format' databases, this * would result in applications `seeing' only the updated * entries. */ key.data = verskey; key.size = sizeof(verskey)-1; data.data = &version; data.size = 1; if ((dp->put)(dp, &key, &data, 0) == -1) error("put"); if ((dp->put)(sdp, &key, &data, 0) == -1) error("put"); } ypcnt = 0; data.data = (u_char *)buf; sdata.data = (u_char *)sbuf; key.data = (u_char *)tbuf; for (cnt = 1; scan(fp, &pwd); ++cnt) { if (!is_comment && (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-')) { yp_enabled = 1; ypcnt++; } if (is_comment) --cnt; #define COMPACT(e) t = e; while ((*p++ = *t++)); #define SCALAR(e) store = htonl((uint32_t)(e)); \ memmove(p, &store, sizeof(store)); \ p += sizeof(store); #define LSCALAR(e) store = HTOL((uint32_t)(e)); \ memmove(p, &store, sizeof(store)); \ p += sizeof(store); #define HTOL(e) (openinfo.lorder == BYTE_ORDER ? \ (uint32_t)(e) : \ bswap32((uint32_t)(e))) if (!is_comment && (!username || (strcmp(username, pwd.pw_name) == 0))) { /* Create insecure data. */ p = buf; COMPACT(pwd.pw_name); COMPACT("*"); SCALAR(pwd.pw_uid); SCALAR(pwd.pw_gid); SCALAR(pwd.pw_change); COMPACT(pwd.pw_class); COMPACT(pwd.pw_gecos); COMPACT(pwd.pw_dir); COMPACT(pwd.pw_shell); SCALAR(pwd.pw_expire); SCALAR(pwd.pw_fields); data.size = p - buf; /* Create secure data. */ p = sbuf; COMPACT(pwd.pw_name); COMPACT(pwd.pw_passwd); SCALAR(pwd.pw_uid); SCALAR(pwd.pw_gid); SCALAR(pwd.pw_change); COMPACT(pwd.pw_class); COMPACT(pwd.pw_gecos); COMPACT(pwd.pw_dir); COMPACT(pwd.pw_shell); SCALAR(pwd.pw_expire); SCALAR(pwd.pw_fields); sdata.size = p - sbuf; /* Store insecure by name. */ tbuf[0] = CURRENT_VERSION(_PW_KEYBYNAME); len = strlen(pwd.pw_name); memmove(tbuf + 1, pwd.pw_name, len); key.size = len + 1; if ((dp->put)(dp, &key, &data, method) == -1) error("put"); /* Store insecure by number. */ tbuf[0] = CURRENT_VERSION(_PW_KEYBYNUM); store = htonl(cnt); memmove(tbuf + 1, &store, sizeof(store)); key.size = sizeof(store) + 1; if ((dp->put)(dp, &key, &data, method) == -1) error("put"); /* Store insecure by uid. */ tbuf[0] = CURRENT_VERSION(_PW_KEYBYUID); store = htonl(pwd.pw_uid); memmove(tbuf + 1, &store, sizeof(store)); key.size = sizeof(store) + 1; if ((dp->put)(dp, &key, &data, methoduid) == -1) error("put"); /* Store secure by name. */ tbuf[0] = CURRENT_VERSION(_PW_KEYBYNAME); len = strlen(pwd.pw_name); memmove(tbuf + 1, pwd.pw_name, len); key.size = len + 1; if ((sdp->put)(sdp, &key, &sdata, method) == -1) error("put"); /* Store secure by number. */ tbuf[0] = CURRENT_VERSION(_PW_KEYBYNUM); store = htonl(cnt); memmove(tbuf + 1, &store, sizeof(store)); key.size = sizeof(store) + 1; if ((sdp->put)(sdp, &key, &sdata, method) == -1) error("put"); /* Store secure by uid. */ tbuf[0] = CURRENT_VERSION(_PW_KEYBYUID); store = htonl(pwd.pw_uid); memmove(tbuf + 1, &store, sizeof(store)); key.size = sizeof(store) + 1; if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1) error("put"); /* Store insecure and secure special plus and special minus */ if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') { tbuf[0] = CURRENT_VERSION(_PW_KEYYPBYNUM); store = htonl(ypcnt); memmove(tbuf + 1, &store, sizeof(store)); key.size = sizeof(store) + 1; if ((dp->put)(dp, &key, &data, method) == -1) error("put"); if ((sdp->put)(sdp, &key, &sdata, method) == -1) error("put"); } /* Create insecure data. (legacy version) */ p = buf; COMPACT(pwd.pw_name); COMPACT("*"); LSCALAR(pwd.pw_uid); LSCALAR(pwd.pw_gid); LSCALAR(pwd.pw_change); COMPACT(pwd.pw_class); COMPACT(pwd.pw_gecos); COMPACT(pwd.pw_dir); COMPACT(pwd.pw_shell); LSCALAR(pwd.pw_expire); LSCALAR(pwd.pw_fields); data.size = p - buf; /* Create secure data. (legacy version) */ p = sbuf; COMPACT(pwd.pw_name); COMPACT(pwd.pw_passwd); LSCALAR(pwd.pw_uid); LSCALAR(pwd.pw_gid); LSCALAR(pwd.pw_change); COMPACT(pwd.pw_class); COMPACT(pwd.pw_gecos); COMPACT(pwd.pw_dir); COMPACT(pwd.pw_shell); LSCALAR(pwd.pw_expire); LSCALAR(pwd.pw_fields); sdata.size = p - sbuf; /* Store insecure by name. */ tbuf[0] = LEGACY_VERSION(_PW_KEYBYNAME); len = strlen(pwd.pw_name); memmove(tbuf + 1, pwd.pw_name, len); key.size = len + 1; if ((dp->put)(dp, &key, &data, method) == -1) error("put"); /* Store insecure by number. */ tbuf[0] = LEGACY_VERSION(_PW_KEYBYNUM); store = HTOL(cnt); memmove(tbuf + 1, &store, sizeof(store)); key.size = sizeof(store) + 1; if ((dp->put)(dp, &key, &data, method) == -1) error("put"); /* Store insecure by uid. */ tbuf[0] = LEGACY_VERSION(_PW_KEYBYUID); store = HTOL(pwd.pw_uid); memmove(tbuf + 1, &store, sizeof(store)); key.size = sizeof(store) + 1; if ((dp->put)(dp, &key, &data, methoduid) == -1) error("put"); /* Store secure by name. */ tbuf[0] = LEGACY_VERSION(_PW_KEYBYNAME); len = strlen(pwd.pw_name); memmove(tbuf + 1, pwd.pw_name, len); key.size = len + 1; if ((sdp->put)(sdp, &key, &sdata, method) == -1) error("put"); /* Store secure by number. */ tbuf[0] = LEGACY_VERSION(_PW_KEYBYNUM); store = HTOL(cnt); memmove(tbuf + 1, &store, sizeof(store)); key.size = sizeof(store) + 1; if ((sdp->put)(sdp, &key, &sdata, method) == -1) error("put"); /* Store secure by uid. */ tbuf[0] = LEGACY_VERSION(_PW_KEYBYUID); store = HTOL(pwd.pw_uid); memmove(tbuf + 1, &store, sizeof(store)); key.size = sizeof(store) + 1; if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1) error("put"); /* Store insecure and secure special plus and special minus */ if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') { tbuf[0] = LEGACY_VERSION(_PW_KEYYPBYNUM); store = HTOL(ypcnt); memmove(tbuf + 1, &store, sizeof(store)); key.size = sizeof(store) + 1; if ((dp->put)(dp, &key, &data, method) == -1) error("put"); if ((sdp->put)(sdp, &key, &sdata, method) == -1) error("put"); } } /* Create original format password file entry */ if (is_comment && makeold){ /* copy comments */ if (fprintf(oldfp, "%s\n", line) < 0) error("write old"); } else if (makeold) { char uidstr[20]; char gidstr[20]; snprintf(uidstr, sizeof(uidstr), "%u", pwd.pw_uid); snprintf(gidstr, sizeof(gidstr), "%u", pwd.pw_gid); if (fprintf(oldfp, "%s:*:%s:%s:%s:%s:%s\n", pwd.pw_name, pwd.pw_fields & _PWF_UID ? uidstr : "", pwd.pw_fields & _PWF_GID ? gidstr : "", pwd.pw_gecos, pwd.pw_dir, pwd.pw_shell) < 0) error("write old"); } } /* If YP enabled, set flag. */ if (yp_enabled) { buf[0] = yp_enabled + 2; data.size = 1; key.size = 1; tbuf[0] = CURRENT_VERSION(_PW_KEYYPENABLED); if ((dp->put)(dp, &key, &data, method) == -1) error("put"); if ((sdp->put)(sdp, &key, &data, method) == -1) error("put"); tbuf[0] = LEGACY_VERSION(_PW_KEYYPENABLED); key.size = 1; if ((dp->put)(dp, &key, &data, method) == -1) error("put"); if ((sdp->put)(sdp, &key, &data, method) == -1) error("put"); } if ((dp->close)(dp) == -1) error("close"); if ((sdp->close)(sdp) == -1) error("close"); if (makeold) { (void)fflush(oldfp); if (fclose(oldfp) == EOF) error("close old"); } /* Set master.passwd permissions, in case caller forgot. */ (void)fchmod(fileno(fp), S_IRUSR|S_IWUSR); /* Install as the real password files. */ (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB); (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB); mv(buf, buf2); (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB); (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _SMP_DB); mv(buf, buf2); if (makeold) { (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _PASSWD); (void)snprintf(buf, sizeof(buf), "%s.orig", pname); mv(buf, buf2); } /* * Move the master password LAST -- chpass(1), passwd(1) and vipw(8) * all use flock(2) on it to block other incarnations of themselves. * The rename means that everything is unlocked, as the original file * can no longer be accessed. */ (void)snprintf(buf, sizeof(buf), "%s/%s", prefix, _MASTERPASSWD); mv(pname, buf); /* * Close locked password file after rename() */ if (fclose(fp) == EOF) error("close fp"); exit(0); } int scan(FILE *fp, struct passwd *pw) { static int lcnt; size_t len; char *p; p = fgetln(fp, &len); if (p == NULL) return (0); ++lcnt; /* * ``... if I swallow anything evil, put your fingers down my * throat...'' * -- The Who */ if (len > 0 && p[len - 1] == '\n') len--; if (len >= sizeof(line) - 1) { warnx("line #%d too long", lcnt); goto fmt; } memcpy(line, p, len); line[len] = '\0'; /* * Ignore comments: ^[ \t]*# */ for (p = line; *p != '\0'; p++) if (*p != ' ' && *p != '\t') break; if (*p == '#' || *p == '\0') { is_comment = 1; return(1); } else is_comment = 0; if (!__pw_scan(line, pw, _PWSCAN_WARN|_PWSCAN_MASTER)) { warnx("at line #%d", lcnt); fmt: errno = EFTYPE; /* XXX */ error(pname); } return (1); } void cp(char *from, char *to, mode_t mode) { static char buf[MAXBSIZE]; int from_fd, rcount, to_fd, wcount; if ((from_fd = open(from, O_RDONLY, 0)) < 0) error(from); if ((to_fd = open(to, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) error(to); while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { wcount = write(to_fd, buf, rcount); if (rcount != wcount || wcount == -1) { int sverrno = errno; (void)snprintf(buf, sizeof(buf), "%s to %s", from, to); errno = sverrno; error(buf); } } if (rcount < 0) { int sverrno = errno; (void)snprintf(buf, sizeof(buf), "%s to %s", from, to); errno = sverrno; error(buf); } } void mv(char *from, char *to) { char buf[MAXPATHLEN]; char *to_dir; int to_dir_fd = -1; /* * Make sure file is safe on disk. To improve performance we will call * fsync() to the directory where file lies */ if (rename(from, to) != 0 || (to_dir = dirname(to)) == NULL || (to_dir_fd = open(to_dir, O_RDONLY|O_DIRECTORY)) == -1 || fsync(to_dir_fd) != 0) { int sverrno = errno; (void)snprintf(buf, sizeof(buf), "%s to %s", from, to); errno = sverrno; if (to_dir_fd != -1) close(to_dir_fd); error(buf); } if (to_dir_fd != -1) close(to_dir_fd); } void error(const char *name) { warn("%s", name); cleanup(); exit(1); } void cleanup(void) { char buf[MAXPATHLEN]; switch(clean) { case FILE_ORIG: (void)snprintf(buf, sizeof(buf), "%s.orig", pname); (void)unlink(buf); /* FALLTHROUGH */ case FILE_SECURE: (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB); (void)unlink(buf); /* FALLTHROUGH */ case FILE_INSECURE: (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB); (void)unlink(buf); } } static void usage(void) { (void)fprintf(stderr, "usage: pwd_mkdb [-BCiLNp] [-d directory] [-s cachesize] [-u username] file\n"); exit(1); } Index: stable/10/usr.sbin/services_mkdb/services_mkdb.c =================================================================== --- stable/10/usr.sbin/services_mkdb/services_mkdb.c (revision 296423) +++ stable/10/usr.sbin/services_mkdb/services_mkdb.c (revision 296424) @@ -1,456 +1,456 @@ /* $NetBSD: services_mkdb.c,v 1.14 2008/04/28 20:24:17 martin Exp $ */ /*- * Copyright (c) 1999 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Luke Mewburn and Christos Zoulas. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "extern.h" static char tname[MAXPATHLEN]; #define PMASK 0xffff #define PROTOMAX 5 static void add(DB *, StringList *, size_t, const char *, size_t *, int); static StringList ***parseservices(const char *, StringList *); static void cleanup(void); static void store(DB *, DBT *, DBT *, int); static void killproto(DBT *); static char *getstring(const char *, size_t, char **, const char *); static size_t getprotoindex(StringList *, const char *); static const char *getprotostr(StringList *, size_t); static const char *mkaliases(StringList *, char *, size_t); static void usage(void); HASHINFO hinfo = { .bsize = 256, .ffactor = 4, .nelem = 32768, .cachesize = 1024, .hash = NULL, .lorder = 0 }; int main(int argc, char *argv[]) { DB *db; int ch; const char *fname = _PATH_SERVICES; const char *dbname = _PATH_SERVICES_DB; int warndup = 1; int unique = 0; int otherflag = 0; int byteorder = 0; size_t cnt = 0; StringList *sl, ***svc; size_t port, proto; char *dbname_dir; int dbname_dir_fd = -1; setprogname(argv[0]); while ((ch = getopt(argc, argv, "blo:qu")) != -1) switch (ch) { case 'b': case 'l': if (byteorder != 0) usage(); byteorder = ch == 'b' ? 4321 : 1234; break; case 'q': otherflag = 1; warndup = 0; break; case 'o': otherflag = 1; dbname = optarg; break; case 'u': unique++; break; case '?': default: usage(); } argc -= optind; argv += optind; if (argc > 1 || (unique && otherflag)) usage(); if (argc == 1) fname = argv[0]; /* Set byte order. */ hinfo.lorder = byteorder; if (unique) uniq(fname); svc = parseservices(fname, sl = sl_init()); if (atexit(cleanup)) err(1, "Cannot install exit handler"); (void)snprintf(tname, sizeof(tname), "%s.tmp", dbname); - db = dbopen(tname, O_RDWR | O_CREAT | O_EXCL | O_SYNC, + db = dbopen(tname, O_RDWR | O_CREAT | O_EXCL, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH), DB_HASH, &hinfo); if (!db) err(1, "Error opening temporary database `%s'", tname); for (port = 0; port < PMASK + 1; port++) { if (svc[port] == NULL) continue; for (proto = 0; proto < PROTOMAX; proto++) { StringList *s; if ((s = svc[port][proto]) == NULL) continue; add(db, s, port, getprotostr(sl, proto), &cnt, warndup); } free(svc[port]); } free(svc); sl_free(sl, 1); if ((db->close)(db)) err(1, "Error closing temporary database `%s'", tname); /* * Make sure file is safe on disk. To improve performance we will call * fsync() to the directory where file lies */ if (rename(tname, dbname) == -1 || (dbname_dir = dirname(dbname)) == NULL || (dbname_dir_fd = open(dbname_dir, O_RDONLY|O_DIRECTORY)) == -1 || fsync(dbname_dir_fd) != 0) { if (dbname_dir_fd != -1) close(dbname_dir_fd); err(1, "Cannot rename `%s' to `%s'", tname, dbname); } if (dbname_dir_fd != -1) close(dbname_dir_fd); return 0; } static void add(DB *db, StringList *sl, size_t port, const char *proto, size_t *cnt, int warndup) { size_t i; char keyb[BUFSIZ], datab[BUFSIZ], abuf[BUFSIZ]; DBT data, key; key.data = keyb; data.data = datab; #ifdef DEBUG (void)printf("add %s %zu %s [ ", sl->sl_str[0], port, proto); for (i = 1; i < sl->sl_cur; i++) (void)printf("%s ", sl->sl_str[i]); (void)printf("]\n"); #endif /* key `indirect key', data `full line' */ data.size = snprintf(datab, sizeof(datab), "%zu", (*cnt)++) + 1; key.size = snprintf(keyb, sizeof(keyb), "%s %zu/%s %s", sl->sl_str[0], port, proto, mkaliases(sl, abuf, sizeof(abuf))) + 1; store(db, &data, &key, warndup); /* key `\377port/proto', data = `indirect key' */ key.size = snprintf(keyb, sizeof(keyb), "\377%zu/%s", port, proto) + 1; store(db, &key, &data, warndup); /* key `\377port', data = `indirect key' */ killproto(&key); store(db, &key, &data, warndup); /* add references for service and all aliases */ for (i = 0; i < sl->sl_cur; i++) { /* key `\376service/proto', data = `indirect key' */ key.size = snprintf(keyb, sizeof(keyb), "\376%s/%s", sl->sl_str[i], proto) + 1; store(db, &key, &data, warndup); /* key `\376service', data = `indirect key' */ killproto(&key); store(db, &key, &data, warndup); } sl_free(sl, 1); } static StringList *** parseservices(const char *fname, StringList *sl) { size_t len, line, pindex; FILE *fp; StringList ***svc, *s; char *p, *ep; if ((fp = fopen(fname, "r")) == NULL) err(1, "Cannot open `%s'", fname); line = 0; if ((svc = calloc(PMASK + 1, sizeof(StringList **))) == NULL) err(1, "Cannot allocate %zu bytes", (size_t)(PMASK + 1)); /* XXX: change NULL to "\0\0#" when fparseln fixed */ for (; (p = fparseln(fp, &len, &line, NULL, 0)) != NULL; free(p)) { char *name, *port, *proto, *aliases, *cp, *alias; unsigned long pnum; if (len == 0) continue; for (cp = p; *cp && isspace((unsigned char)*cp); cp++) continue; if (*cp == '\0' || *cp == '#') continue; if ((name = getstring(fname, line, &cp, "name")) == NULL) continue; if ((port = getstring(fname, line, &cp, "port")) == NULL) continue; if (cp) { for (aliases = cp; *cp && *cp != '#'; cp++) continue; if (*cp) *cp = '\0'; } else aliases = NULL; proto = strchr(port, '/'); if (proto == NULL || proto[1] == '\0') { warnx("%s, %zu: no protocol found", fname, line); continue; } *proto++ = '\0'; errno = 0; pnum = strtoul(port, &ep, 0); if (*port == '\0' || *ep != '\0') { warnx("%s, %zu: invalid port `%s'", fname, line, port); continue; } if ((errno == ERANGE && pnum == ULONG_MAX) || pnum > PMASK) { warnx("%s, %zu: port too big `%s'", fname, line, port); continue; } if (svc[pnum] == NULL) { svc[pnum] = calloc(PROTOMAX, sizeof(StringList *)); if (svc[pnum] == NULL) err(1, "Cannot allocate %zu bytes", (size_t)PROTOMAX); } pindex = getprotoindex(sl, proto); if (svc[pnum][pindex] == NULL) s = svc[pnum][pindex] = sl_init(); else s = svc[pnum][pindex]; /* build list of aliases */ if (sl_find(s, name) == NULL) { char *p2; if ((p2 = strdup(name)) == NULL) err(1, "Cannot copy string"); (void)sl_add(s, p2); } if (aliases) { while ((alias = strsep(&aliases, " \t")) != NULL) { if (alias[0] == '\0') continue; if (sl_find(s, alias) == NULL) { char *p2; if ((p2 = strdup(alias)) == NULL) err(1, "Cannot copy string"); (void)sl_add(s, p2); } } } } (void)fclose(fp); return svc; } /* * cleanup(): Remove temporary files upon exit */ static void cleanup(void) { if (tname[0]) (void)unlink(tname); } static char * getstring(const char *fname, size_t line, char **cp, const char *tag) { char *str; while ((str = strsep(cp, " \t")) != NULL && *str == '\0') continue; if (str == NULL) warnx("%s, %zu: no %s found", fname, line, tag); return str; } static void killproto(DBT *key) { char *p, *d = key->data; if ((p = strchr(d, '/')) == NULL) abort(); *p++ = '\0'; key->size = p - d; } static void store(DB *db, DBT *key, DBT *data, int warndup) { #ifdef DEBUG int k = key->size - 1; int d = data->size - 1; (void)printf("store [%*.*s] [%*.*s]\n", k, k, (char *)key->data + 1, d, d, (char *)data->data + 1); #endif switch ((db->put)(db, key, data, R_NOOVERWRITE)) { case 0: break; case 1: if (warndup) warnx("duplicate service `%s'", &((char *)key->data)[1]); break; case -1: err(1, "put"); break; default: abort(); break; } } static size_t getprotoindex(StringList *sl, const char *str) { size_t i; char *p; for (i= 0; i < sl->sl_cur; i++) if (strcmp(sl->sl_str[i], str) == 0) return i; if (i == PROTOMAX) errx(1, "Ran out of protocols adding `%s';" " recompile with larger PROTOMAX", str); if ((p = strdup(str)) == NULL) err(1, "Cannot copy string"); (void)sl_add(sl, p); return i; } static const char * getprotostr(StringList *sl, size_t i) { assert(i < sl->sl_cur); return sl->sl_str[i]; } static const char * mkaliases(StringList *sl, char *buf, size_t len) { size_t nc, i, pos; buf[0] = 0; for (i = 1, pos = 0; i < sl->sl_cur; i++) { nc = strlcpy(buf + pos, sl->sl_str[i], len); if (nc >= len) goto out; pos += nc; len -= nc; nc = strlcpy(buf + pos, " ", len); if (nc >= len) goto out; pos += nc; len -= nc; } return buf; out: warn("aliases for `%s' truncated", sl->sl_str[0]); return buf; } static void usage(void) { (void)fprintf(stderr, "Usage:\t%s [-b | -l] [-q] [-o ] []\n" "\t%s -u []\n", getprogname(), getprogname()); exit(1); } Index: stable/10 =================================================================== --- stable/10 (revision 296423) +++ stable/10 (revision 296424) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r295465,295800,295924-295925