Home | History | Annotate | Line # | Download | only in sdpd
      1  1.2  plunky /*	$NetBSD: db.c,v 1.2 2015/11/24 21:11:39 plunky Exp $	*/
      2  1.1  plunky 
      3  1.1  plunky /*-
      4  1.1  plunky  * Copyright (c) 2009 The NetBSD Foundation, Inc.
      5  1.1  plunky  * All rights reserved.
      6  1.1  plunky  *
      7  1.1  plunky  * This code is derived from software contributed to The NetBSD Foundation
      8  1.1  plunky  * by Iain Hibbert.
      9  1.1  plunky  *
     10  1.1  plunky  * Redistribution and use in source and binary forms, with or without
     11  1.1  plunky  * modification, are permitted provided that the following conditions
     12  1.1  plunky  * are met:
     13  1.1  plunky  * 1. Redistributions of source code must retain the above copyright
     14  1.1  plunky  *    notice, this list of conditions and the following disclaimer.
     15  1.1  plunky  * 2. Redistributions in binary form must reproduce the above copyright
     16  1.1  plunky  *    notice, this list of conditions and the following disclaimer in the
     17  1.1  plunky  *    documentation and/or other materials provided with the distribution.
     18  1.1  plunky  *
     19  1.1  plunky  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  1.1  plunky  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  1.1  plunky  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  1.1  plunky  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  1.1  plunky  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  1.1  plunky  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  1.1  plunky  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  1.1  plunky  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  1.1  plunky  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  1.1  plunky  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  1.1  plunky  * POSSIBILITY OF SUCH DAMAGE.
     30  1.1  plunky  */
     31  1.1  plunky 
     32  1.1  plunky #include <sys/cdefs.h>
     33  1.2  plunky __RCSID("$NetBSD: db.c,v 1.2 2015/11/24 21:11:39 plunky Exp $");
     34  1.1  plunky 
     35  1.1  plunky #include <bluetooth.h>
     36  1.1  plunky #include <sdp.h>
     37  1.1  plunky #include <stdbool.h>
     38  1.1  plunky #include <stdlib.h>
     39  1.1  plunky #include <string.h>
     40  1.1  plunky #include <uuid.h>
     41  1.1  plunky 
     42  1.1  plunky #include "sdpd.h"
     43  1.1  plunky 
     44  1.1  plunky /*
     45  1.1  plunky  * Using a prebuilt service record means that providing ServerState
     46  1.1  plunky  * and a non-hardcoded ProviderName are difficult. Look into that later.
     47  1.1  plunky  */
     48  1.1  plunky 
     49  1.1  plunky /* ServiceDiscoveryServer service record */
     50  1.1  plunky static uint8_t sds_data[] = {
     51  1.1  plunky 	0x09, 0x00, 0x00,	//  uint16	ServiceRecordHandle
     52  1.1  plunky 	0x0a, 0x00, 0x00, 0x00,	//  uint32	0x00000000
     53  1.1  plunky 	0x00,
     54  1.1  plunky 
     55  1.1  plunky 	0x09, 0x00, 0x01,	//  uint16	ServiceClassIDList
     56  1.1  plunky 	0x35, 0x03,		//  seq8(3)
     57  1.1  plunky 	0x19, 0x10, 0x00,	//   uuid16	ServiceDiscoveryServer
     58  1.1  plunky 
     59  1.1  plunky 	0x09, 0x00, 0x04,	//  uint16	ProtocolDescriptorList
     60  1.1  plunky 	0x35, 0x0d,		//  seq8(13)
     61  1.1  plunky 	0x35, 0x06,		//   seq8(6)
     62  1.1  plunky 	0x19, 0x01, 0x00,	//    uuid16	L2CAP
     63  1.1  plunky 	0x09, 0x00, 0x01,	//    uint16	L2CAP_PSM_SDP
     64  1.1  plunky 	0x35, 0x03,		//   seq8(3)
     65  1.1  plunky 	0x19, 0x00, 0x01,	//    uuid16	SDP
     66  1.1  plunky 
     67  1.1  plunky 	0x09, 0x00, 0x05,	//  uint16	BrowseGroupList
     68  1.1  plunky 	0x35, 0x03,		//  seq8(3)
     69  1.1  plunky 	0x19, 0x10, 0x02,	//   uuid16	PublicBrowseGroup
     70  1.1  plunky 
     71  1.1  plunky 	0x09, 0x00, 0x06,	//  uint16	LanguageBaseAttributeIDList
     72  1.1  plunky 	0x35, 0x09,		//  seq8(9)
     73  1.1  plunky 	0x09, 0x65, 0x6e,	//   uint16	0x656e	("en")
     74  1.1  plunky 	0x09, 0x00, 0x6a,	//   uint16	106	(UTF-8)
     75  1.1  plunky 	0x09, 0x01, 0x00,	//   uint16	PrimaryLanguageBaseID
     76  1.1  plunky 
     77  1.1  plunky 	0x09, 0x01, 0x00,	//  uint16	PrimaryLanguageBaseID + ServiceNameOffset
     78  1.1  plunky 	0x25, 0x1b, 0x42, 0x6c,	//  str8(27)	"Bluetooth service discovery"
     79  1.1  plunky 	0x75, 0x65, 0x74, 0x6f,
     80  1.1  plunky 	0x6f, 0x74, 0x68, 0x20,
     81  1.1  plunky 	0x73, 0x65, 0x72, 0x76,
     82  1.1  plunky 	0x69, 0x63, 0x65, 0x20,
     83  1.1  plunky 	0x64, 0x69, 0x73, 0x63,
     84  1.1  plunky 	0x6f, 0x76, 0x65, 0x72,
     85  1.1  plunky 	0x79,
     86  1.1  plunky 
     87  1.1  plunky 	0x09, 0x01, 0x02,	//  uint16	PrimaryLanguageBaseID + ProviderNameOffset
     88  1.1  plunky 	0x25, 0x06, 0x4e, 0x65,	//  str8(6)	"NetBSD"
     89  1.1  plunky 	0x74, 0x42, 0x53, 0x44,
     90  1.1  plunky 
     91  1.1  plunky 	0x09, 0x02, 0x00,	//  uint16	VersionNumberList
     92  1.1  plunky 	0x35, 0x03,		//  seq8(3)
     93  1.1  plunky 	0x09, 0x01, 0x00,	//   uint16	v1.0
     94  1.1  plunky };
     95  1.1  plunky 
     96  1.1  plunky /* BrowseGroupDescriptor service record */
     97  1.1  plunky static uint8_t bgd_data[] = {
     98  1.1  plunky 	0x09, 0x00, 0x00,	//  uint16	ServiceRecordHandle
     99  1.2  plunky 	0x0a, 0x00, 0x01, 0x00,	//  uint32	0x00010000
    100  1.2  plunky 	0x00,
    101  1.1  plunky 
    102  1.1  plunky 	0x09, 0x00, 0x01,	//  uint16	ServiceClassIDList
    103  1.1  plunky 	0x35, 0x03,		//  seq8(3)
    104  1.1  plunky 	0x19, 0x10, 0x01,	//   uuid16	BrowseGroupDescriptor
    105  1.1  plunky 
    106  1.1  plunky 	0x09, 0x00, 0x06,	//  uint16	LanguageBaseAttributeIDList
    107  1.1  plunky 	0x35, 0x09,		//  seq8(9)
    108  1.1  plunky 	0x09, 0x65, 0x6e,	//   uint16	0x656e	("en")
    109  1.1  plunky 	0x09, 0x00, 0x6a,	//   uint16	106	(UTF-8)
    110  1.1  plunky 	0x09, 0x01, 0x00,	//   uint16	PrimaryLanguageBaseID
    111  1.1  plunky 
    112  1.1  plunky 	0x09, 0x01, 0x00,	//  uint16	PrimaryLanguageBaseID + ServiceNameOffset
    113  1.1  plunky 	0x25, 0x12, 0x50, 0x75,	//  str8(18)	"Public Browse Root"
    114  1.1  plunky 	0x62, 0x6c, 0x69, 0x63,
    115  1.1  plunky 	0x20, 0x42, 0x72, 0x6f,
    116  1.1  plunky 	0x77, 0x73, 0x65, 0x20,
    117  1.1  plunky 	0x52, 0x6f, 0x6f, 0x74,
    118  1.1  plunky 
    119  1.1  plunky 	0x09, 0x02, 0x00,	//  uint16	GroupID
    120  1.1  plunky 	0x19, 0x10, 0x02,	//  uuid16	PublicBrowseRoot
    121  1.1  plunky };
    122  1.1  plunky 
    123  1.1  plunky /*
    124  1.1  plunky  * Initialise the record database with the ServiceDiscoveryServer
    125  1.1  plunky  * and BrowseGroupDescriptor records
    126  1.1  plunky  */
    127  1.1  plunky bool
    128  1.1  plunky db_init(server_t *srv)
    129  1.1  plunky {
    130  1.1  plunky 	sdp_data_t d;
    131  1.1  plunky 
    132  1.1  plunky 	LIST_INIT(&srv->rlist);
    133  1.2  plunky 	srv->handle = 0x00010000; /* values 0x00000001->0x0000FFFF are reserved */
    134  1.1  plunky 
    135  1.1  plunky 	d.next = sds_data;
    136  1.1  plunky 	d.end = sds_data + sizeof(sds_data);
    137  1.2  plunky 	if (!db_create(srv, -1, BDADDR_ANY, 0x00000000, &d))
    138  1.1  plunky 		return false;
    139  1.1  plunky 
    140  1.1  plunky 	d.next = bgd_data;
    141  1.1  plunky 	d.end = bgd_data + sizeof(bgd_data);
    142  1.1  plunky 	if (!db_create(srv, -1, BDADDR_ANY, srv->handle++, &d))
    143  1.1  plunky 		return false;
    144  1.1  plunky 
    145  1.1  plunky 	return true;
    146  1.1  plunky }
    147  1.1  plunky 
    148  1.1  plunky /*
    149  1.1  plunky  * Iterate through records selected by fd.  rec should point to a NULL
    150  1.1  plunky  * value to start the iteration, and false will be returned when there
    151  1.1  plunky  * are no more records to return.
    152  1.1  plunky  */
    153  1.1  plunky bool
    154  1.1  plunky db_next(server_t *srv, int fd, record_t **rec)
    155  1.1  plunky {
    156  1.1  plunky 	record_t *r;
    157  1.1  plunky 
    158  1.1  plunky 	if (*rec == NULL)
    159  1.1  plunky 		r = LIST_FIRST(&srv->rlist);
    160  1.1  plunky 	else
    161  1.1  plunky 		r = LIST_NEXT(*rec, next);
    162  1.1  plunky 
    163  1.1  plunky 	while (r != NULL && !FD_ISSET(fd, &r->refset))
    164  1.1  plunky 		r = LIST_NEXT(r, next);
    165  1.1  plunky 
    166  1.1  plunky 	*rec = r;
    167  1.1  plunky 	return (r == NULL) ? false : true;
    168  1.1  plunky }
    169  1.1  plunky 
    170  1.1  plunky /*
    171  1.1  plunky  * Match a ServiceRecord against a UUID. Note that because we already
    172  1.1  plunky  * know that the record data is valid, we don't need to recurse here
    173  1.1  plunky  * and can just skip over SEQ and ALT headers. Return true if equivalent
    174  1.1  plunky  * UUID is found.
    175  1.1  plunky  */
    176  1.1  plunky static bool
    177  1.1  plunky db_match_uuid(record_t *rec, uuid_t *uuid)
    178  1.1  plunky {
    179  1.1  plunky 	uint8_t *p = rec->data.next;
    180  1.1  plunky 	uuid_t u;
    181  1.1  plunky 
    182  1.1  plunky 	while (p < rec->data.end) {
    183  1.1  plunky 		switch(*p++) {
    184  1.1  plunky 		case SDP_DATA_NIL:
    185  1.1  plunky 			break;
    186  1.1  plunky 
    187  1.1  plunky 		case SDP_DATA_BOOL:
    188  1.1  plunky 		case SDP_DATA_INT8:
    189  1.1  plunky 		case SDP_DATA_UINT8:
    190  1.1  plunky 		case SDP_DATA_SEQ8:
    191  1.1  plunky 		case SDP_DATA_ALT8:
    192  1.1  plunky 			p += 1;
    193  1.1  plunky 			break;
    194  1.1  plunky 
    195  1.1  plunky 		case SDP_DATA_INT16:
    196  1.1  plunky 		case SDP_DATA_UINT16:
    197  1.1  plunky 		case SDP_DATA_SEQ16:
    198  1.1  plunky 		case SDP_DATA_ALT16:
    199  1.1  plunky 			p += 2;
    200  1.1  plunky 			break;
    201  1.1  plunky 
    202  1.1  plunky 		case SDP_DATA_INT32:
    203  1.1  plunky 		case SDP_DATA_UINT32:
    204  1.1  plunky 		case SDP_DATA_SEQ32:
    205  1.1  plunky 		case SDP_DATA_ALT32:
    206  1.1  plunky 			p += 4;
    207  1.1  plunky 			break;
    208  1.1  plunky 
    209  1.1  plunky 		case SDP_DATA_INT64:
    210  1.1  plunky 		case SDP_DATA_UINT64:
    211  1.1  plunky 			p += 8;
    212  1.1  plunky 			break;
    213  1.1  plunky 
    214  1.1  plunky 		case SDP_DATA_INT128:
    215  1.1  plunky 		case SDP_DATA_UINT128:
    216  1.1  plunky 			p += 16;
    217  1.1  plunky 			break;
    218  1.1  plunky 
    219  1.1  plunky 		case SDP_DATA_STR8:
    220  1.1  plunky 		case SDP_DATA_URL8:
    221  1.1  plunky 			p += 1 + *p;
    222  1.1  plunky 			break;
    223  1.1  plunky 
    224  1.1  plunky 		case SDP_DATA_STR16:
    225  1.1  plunky 		case SDP_DATA_URL16:
    226  1.1  plunky 			p += 2 + be16dec(p);
    227  1.1  plunky 			break;
    228  1.1  plunky 
    229  1.1  plunky 		case SDP_DATA_STR32:
    230  1.1  plunky 		case SDP_DATA_URL32:
    231  1.1  plunky 			p += 4 + be32dec(p);
    232  1.1  plunky 			break;
    233  1.1  plunky 
    234  1.1  plunky 		case SDP_DATA_UUID16:
    235  1.1  plunky 			u = BLUETOOTH_BASE_UUID;
    236  1.1  plunky 			u.time_low = be16dec(p);
    237  1.1  plunky 
    238  1.1  plunky 			if (uuid_equal(&u, uuid, NULL))
    239  1.1  plunky 				return true;
    240  1.1  plunky 
    241  1.1  plunky 			p += 2;
    242  1.1  plunky 			break;
    243  1.1  plunky 
    244  1.1  plunky 		case SDP_DATA_UUID32:
    245  1.1  plunky 			u = BLUETOOTH_BASE_UUID;
    246  1.1  plunky 			u.time_low = be32dec(p);
    247  1.1  plunky 
    248  1.1  plunky 			if (uuid_equal(&u, uuid, NULL))
    249  1.1  plunky 				return true;
    250  1.1  plunky 
    251  1.1  plunky 			p += 4;
    252  1.1  plunky 			break;
    253  1.1  plunky 
    254  1.1  plunky 		case SDP_DATA_UUID128:
    255  1.1  plunky 			uuid_dec_be(p, &u);
    256  1.1  plunky 
    257  1.1  plunky 			if (uuid_equal(&u, uuid, NULL))
    258  1.1  plunky 				return true;
    259  1.1  plunky 
    260  1.1  plunky 			p += 16;
    261  1.1  plunky 			break;
    262  1.1  plunky 
    263  1.1  plunky 		default:
    264  1.1  plunky 			return false;
    265  1.1  plunky 		}
    266  1.1  plunky 	}
    267  1.1  plunky 
    268  1.1  plunky 	return false;
    269  1.1  plunky }
    270  1.1  plunky 
    271  1.1  plunky /*
    272  1.1  plunky  * Select ServiceRecords matching ServiceSearchPattern
    273  1.1  plunky  *
    274  1.1  plunky  * A record is selected when it is visible to the client and
    275  1.1  plunky  * contains each and every UUID from the ServiceSearchPattern
    276  1.1  plunky  */
    277  1.1  plunky void
    278  1.1  plunky db_select_ssp(server_t *srv, int fd, sdp_data_t *ssp)
    279  1.1  plunky {
    280  1.1  plunky 	record_t *r;
    281  1.1  plunky 	sdp_data_t s;
    282  1.1  plunky 	uuid_t u;
    283  1.1  plunky 
    284  1.1  plunky 	LIST_FOREACH(r, &srv->rlist, next) {
    285  1.1  plunky 		if (!r->valid)
    286  1.1  plunky 			continue;
    287  1.1  plunky 
    288  1.1  plunky 		if (!srv->fdidx[fd].control
    289  1.1  plunky 		    && !bdaddr_any(&r->bdaddr)
    290  1.1  plunky 		    && !bdaddr_same(&r->bdaddr, &srv->fdidx[fd].bdaddr))
    291  1.1  plunky 			continue;
    292  1.1  plunky 
    293  1.1  plunky 		s = *ssp;
    294  1.1  plunky 		for (;;) {
    295  1.1  plunky 			if (!sdp_get_uuid(&s, &u)) {
    296  1.1  plunky 				/* matched all UUIDs */
    297  1.1  plunky 				FD_SET(fd, &r->refset);
    298  1.1  plunky 				r->refcnt++;
    299  1.1  plunky 				break;
    300  1.1  plunky 			}
    301  1.1  plunky 
    302  1.1  plunky 			if (!db_match_uuid(r, &u)) {
    303  1.1  plunky 				/* does not match UUID */
    304  1.1  plunky 				break;
    305  1.1  plunky 			}
    306  1.1  plunky 		}
    307  1.1  plunky 	}
    308  1.1  plunky }
    309  1.1  plunky 
    310  1.1  plunky /*
    311  1.1  plunky  * Select a ServiceRecord given the RecordHandle.
    312  1.1  plunky  */
    313  1.1  plunky void
    314  1.1  plunky db_select_handle(server_t *srv, int fd, uint32_t handle)
    315  1.1  plunky {
    316  1.1  plunky 	record_t *r;
    317  1.1  plunky 
    318  1.1  plunky 	LIST_FOREACH(r, &srv->rlist, next) {
    319  1.1  plunky 		if (!r->valid)
    320  1.1  plunky 			continue;
    321  1.1  plunky 
    322  1.1  plunky 		if (!srv->fdidx[fd].control
    323  1.1  plunky 		    && !bdaddr_any(&r->bdaddr)
    324  1.1  plunky 		    && !bdaddr_same(&r->bdaddr, &srv->fdidx[fd].bdaddr))
    325  1.1  plunky 			continue;
    326  1.1  plunky 
    327  1.1  plunky 		if (handle == r->handle) {
    328  1.1  plunky 			FD_SET(fd, &r->refset);
    329  1.1  plunky 			r->refcnt++;
    330  1.1  plunky 			break;
    331  1.1  plunky 		}
    332  1.1  plunky 	}
    333  1.1  plunky }
    334  1.1  plunky 
    335  1.1  plunky /*
    336  1.1  plunky  * Create a record and insert in server record list in ascending handle
    337  1.1  plunky  * order. Where a selectable record exists with the same handle number,
    338  1.1  plunky  * it will be expired.
    339  1.1  plunky  */
    340  1.1  plunky bool
    341  1.1  plunky db_create(server_t *srv, int fd, const bdaddr_t *bdaddr, uint32_t handle, sdp_data_t *data)
    342  1.1  plunky {
    343  1.1  plunky 	record_t *n, *r, *rec;
    344  1.1  plunky 	sdp_data_t d, v;
    345  1.1  plunky 	uint16_t a;
    346  1.1  plunky 	size_t len;
    347  1.1  plunky 
    348  1.1  plunky 	d = *data;
    349  1.1  plunky 	if (!sdp_get_attr(&d, &a, &v)
    350  1.1  plunky 	    || a != SDP_ATTR_SERVICE_RECORD_HANDLE
    351  1.1  plunky 	    || sdp_data_type(&v) != SDP_DATA_UINT32)
    352  1.1  plunky 		return false;
    353  1.1  plunky 
    354  1.1  plunky 	sdp_set_uint(&v, handle);
    355  1.1  plunky 
    356  1.1  plunky 	len = data->end - data->next;
    357  1.1  plunky 	rec = malloc(sizeof(record_t) + len);
    358  1.1  plunky 	if (rec == NULL)
    359  1.1  plunky 		return false;
    360  1.1  plunky 
    361  1.1  plunky 	memset(rec, 0, sizeof(record_t));
    362  1.1  plunky 	FD_ZERO(&rec->refset);
    363  1.1  plunky 	rec->handle = handle;
    364  1.1  plunky 	rec->valid = true;
    365  1.1  plunky 	rec->fd = fd;
    366  1.1  plunky 	bdaddr_copy(&rec->bdaddr, bdaddr);
    367  1.1  plunky 	rec->data.next = rec->ext;
    368  1.1  plunky 	rec->data.end = rec->ext + len;
    369  1.1  plunky 	memcpy(rec->ext, data->next, len);
    370  1.1  plunky 
    371  1.1  plunky 	/*
    372  1.1  plunky 	 * Note, this does not handle the case where we expire
    373  1.1  plunky 	 * the first record on the list, as that won't happen.
    374  1.1  plunky 	 */
    375  1.1  plunky 	n = LIST_FIRST(&srv->rlist);
    376  1.1  plunky 	if (n != NULL) {
    377  1.1  plunky 		do {
    378  1.1  plunky 			r = n;
    379  1.1  plunky 			n = LIST_NEXT(r, next);
    380  1.1  plunky 		} while (n != NULL && n->handle < handle);
    381  1.1  plunky 
    382  1.1  plunky 		if (n != NULL && n->valid && n->handle == handle) {
    383  1.1  plunky 			if (n->refcnt-- == 0) {
    384  1.1  plunky 				LIST_REMOVE(n, next);
    385  1.1  plunky 				free(n);
    386  1.1  plunky 			} else {
    387  1.1  plunky 				n->valid = false;
    388  1.1  plunky 				n->fd = -1;
    389  1.1  plunky 			}
    390  1.1  plunky 		}
    391  1.1  plunky 
    392  1.1  plunky 		LIST_INSERT_AFTER(r, rec, next);
    393  1.1  plunky 	} else {
    394  1.1  plunky 		LIST_INSERT_HEAD(&srv->rlist, rec, next);
    395  1.1  plunky 	}
    396  1.1  plunky 
    397  1.1  plunky 	return true;
    398  1.1  plunky }
    399  1.1  plunky 
    400  1.1  plunky /*
    401  1.1  plunky  * Unselect any ServiceRecords selected by fd
    402  1.1  plunky  */
    403  1.1  plunky void
    404  1.1  plunky db_unselect(server_t *srv, int fd)
    405  1.1  plunky {
    406  1.1  plunky 	record_t *n, *r;
    407  1.1  plunky 
    408  1.1  plunky 	n = LIST_FIRST(&srv->rlist);
    409  1.1  plunky 	while (n != NULL) {
    410  1.1  plunky 		r = n;
    411  1.1  plunky 		n = LIST_NEXT(r, next);
    412  1.1  plunky 
    413  1.1  plunky 		if (FD_ISSET(fd, &r->refset)) {
    414  1.1  plunky 			if (r->refcnt-- == 0) {
    415  1.1  plunky 				LIST_REMOVE(r, next);
    416  1.1  plunky 				free(r);
    417  1.1  plunky 			} else {
    418  1.1  plunky 				FD_CLR(fd, &r->refset);
    419  1.1  plunky 			}
    420  1.1  plunky 		}
    421  1.1  plunky 	}
    422  1.1  plunky }
    423  1.1  plunky 
    424  1.1  plunky /*
    425  1.1  plunky  * Invalidate or release all records owned by fd
    426  1.1  plunky  */
    427  1.1  plunky void
    428  1.1  plunky db_release(server_t *srv, int fd)
    429  1.1  plunky {
    430  1.1  plunky 	record_t *n, *r;
    431  1.1  plunky 
    432  1.1  plunky 	n = LIST_FIRST(&srv->rlist);
    433  1.1  plunky 	while (n != NULL) {
    434  1.1  plunky 		r = n;
    435  1.1  plunky 		n = LIST_NEXT(r, next);
    436  1.1  plunky 
    437  1.1  plunky 		if (r->fd == fd) {
    438  1.1  plunky 			if (r->refcnt-- == 0) {
    439  1.1  plunky 				LIST_REMOVE(r, next);
    440  1.1  plunky 				free(r);
    441  1.1  plunky 			} else {
    442  1.1  plunky 				r->valid = false;
    443  1.1  plunky 				r->fd = -1;
    444  1.1  plunky 			}
    445  1.1  plunky 		}
    446  1.1  plunky 	}
    447  1.1  plunky }
    448