示例#1
0
文件: reg_db.c 项目: AllardJ/Tomato
static int regdb_unpack_values(REGVAL_CTR *values, char *buf, int buflen)
{
	int 		len = 0;
	uint32		type;
	pstring		valuename;
	uint32		size;
	uint8		*data_p;
	uint32 		num_values = 0;
	int 		i;
	
	
	
	/* loop and unpack the rest of the registry values */
	
	len += tdb_unpack(buf+len, buflen-len, "d", &num_values);
	
	for ( i=0; i<num_values; i++ ) {
		/* unpack the next regval */
		
		type = REG_NONE;
		size = 0;
		data_p = NULL;
		len += tdb_unpack(buf+len, buflen-len, "fdB",
				  valuename,
				  &type,
				  &size,
				  &data_p);
				
		/* add the new value. Paranoid protective code -- make sure data_p is valid */

		if ( size && data_p ) {
			regval_ctr_addvalue( values, valuename, type, (const char *)data_p, size );
			SAFE_FREE(data_p); /* 'B' option to tdb_unpack does a malloc() */
		}

		DEBUG(8,("specific: [%s], len: %d\n", valuename, size));
	}

	return len;
}
示例#2
0
文件: registry.c 项目: sangfo/WMI_cmd
static int regdb_unpack_values(TDB_CONTEXT *tdb, TALLOC_CTX *ctx, struct samba3_regkey *key, TDB_DATA data )
{
    int 		len = 0;
    uint32_t	type;
    uint32_t	size;
    uint8_t *data_p;
    uint32_t	num_values = 0;
    int 		i;
    fstring valuename;

    /* loop and unpack the rest of the registry values */

    len += tdb_unpack(tdb, (char *)data.dptr+len, data.dsize-len, "d", &num_values);

    for ( i=0; i<num_values; i++ ) {
        struct samba3_regval val;
        /* unpack the next regval */

        type = REG_NONE;
        size = 0;
        data_p = NULL;
        len += tdb_unpack(tdb, (char *)data.dptr+len, data.dsize-len, "fdB",
                          valuename,
                          &val.type,
                          &size,
                          &data_p);
        val.name = talloc_strdup(ctx, valuename);
        val.data = data_blob_talloc(ctx, data_p, size);

        key->values = talloc_realloc(ctx, key->values, struct samba3_regval, key->value_count+1);
        key->values[key->value_count] = val;
        key->value_count++;
    }

    return len;
}
示例#3
0
static int printer_list_clean_fn(struct db_record *rec, void *private_data)
{
    struct printer_list_clean_state *state =
        (struct printer_list_clean_state *)private_data;
    uint32_t time_h, time_l;
    time_t refresh;
    char *name;
    char *comment;
    char *location;
    int ret;
    TDB_DATA key;
    TDB_DATA value;

    key = dbwrap_record_get_key(rec);

    /* skip anything that does not contain PL_DATA_FORMAT data */
    if (strncmp((char *)key.dptr,
                PL_KEY_PREFIX, sizeof(PL_KEY_PREFIX)-1)) {
        return 0;
    }

    value = dbwrap_record_get_value(rec);

    ret = tdb_unpack(value.dptr, value.dsize,
                     PL_DATA_FORMAT, &time_h, &time_l, &name, &comment,
                     &location);
    if (ret == -1) {
        DEBUG(1, ("Failed to un pack printer data"));
        state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
        return -1;
    }

    SAFE_FREE(name);
    SAFE_FREE(comment);
    SAFE_FREE(location);

    refresh = (time_t)(((uint64_t)time_h << 32) + time_l);

    if (refresh < state->last_refresh) {
        state->status = dbwrap_record_delete(rec);
        if (!NT_STATUS_IS_OK(state->status)) {
            return -1;
        }
    }

    return 0;
}
示例#4
0
/* if we can't read the cache, oh well, no need to return anything */
LOGIN_CACHE * login_cache_read(struct samu *sampass)
{
	char *keystr;
	TDB_DATA databuf;
	LOGIN_CACHE *entry;

	if (!login_cache_init())
		return NULL;

	if (pdb_get_nt_username(sampass) == NULL) {
		return NULL;
	}

	keystr = SMB_STRDUP(pdb_get_nt_username(sampass));
	if (!keystr || !keystr[0]) {
		SAFE_FREE(keystr);
		return NULL;
	}

	DEBUG(7, ("Looking up login cache for user %s\n",
		  keystr));
	databuf = tdb_fetch_bystring(cache, keystr);
	SAFE_FREE(keystr);

	if (!(entry = SMB_MALLOC_P(LOGIN_CACHE))) {
		DEBUG(1, ("Unable to allocate cache entry buffer!\n"));
		SAFE_FREE(databuf.dptr);
		return NULL;
	}

	if (tdb_unpack (databuf.dptr, databuf.dsize, SAM_CACHE_FORMAT,
			&entry->entry_timestamp, &entry->acct_ctrl, 
			&entry->bad_password_count, 
			&entry->bad_password_time) == -1) {
		DEBUG(7, ("No cache entry found\n"));
		SAFE_FREE(entry);
		SAFE_FREE(databuf.dptr);
		return NULL;
	}

	SAFE_FREE(databuf.dptr);

	DEBUG(5, ("Found login cache entry: timestamp %12u, flags 0x%x, count %d, time %12u\n",
		  (unsigned int)entry->entry_timestamp, entry->acct_ctrl, 
		  entry->bad_password_count, (unsigned int)entry->bad_password_time));
	return entry;
}
static bool get_group_map_from_sid(struct dom_sid sid, GROUP_MAP *map)
{
	TDB_DATA dbuf;
	char *key;
	int ret = 0;
	NTSTATUS status;
	fstring nt_name;
	fstring comment;

	/* the key is the SID, retrieving is direct */

	key = group_mapping_key(talloc_tos(), &sid);
	if (key == NULL) {
		return false;
	}

	status = dbwrap_fetch_bystring(db, key, key, &dbuf);
	if (!NT_STATUS_IS_OK(status)) {
		TALLOC_FREE(key);
		return false;
	}

	ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "ddff",
			&map->gid, &map->sid_name_use,
			&nt_name, &comment);

	TALLOC_FREE(key);

	if ( ret == -1 ) {
		DEBUG(3,("get_group_map_from_sid: tdb_unpack failure\n"));
		return false;
	}

	sid_copy(&map->sid, &sid);

	map->nt_name = talloc_strdup(map, nt_name);
	if (!map->nt_name) {
		return false;
	}
	map->comment = talloc_strdup(map, comment);
	if (!map->comment) {
		return false;
	}

	return true;
}
示例#6
0
static bool dbrec2map(const struct db_record *rec, GROUP_MAP *map)
{
	if ((rec->key.dsize < strlen(GROUP_PREFIX))
	    || (strncmp((char *)rec->key.dptr, GROUP_PREFIX,
			GROUP_PREFIX_LEN) != 0)) {
		return False;
	}

	if (!string_to_sid(&map->sid, (const char *)rec->key.dptr
			   + GROUP_PREFIX_LEN)) {
		return False;
	}

	return tdb_unpack(rec->value.dptr, rec->value.dsize, "ddff",
			  &map->gid, &map->sid_name_use, &map->nt_name,
			  &map->comment) != -1;
}
示例#7
0
/*
  upgrade one group mapping record from the old tdb format
*/
static int upgrade_map_record(TDB_CONTEXT *tdb_ctx, TDB_DATA key, 
			      TDB_DATA data, void *state)
{
	int ret;
	GROUP_MAP map;

	if (strncmp((char *)key.dptr, GROUP_PREFIX, 
		    MIN(key.dsize, strlen(GROUP_PREFIX))) != 0) {
		return 0;
	}

	if (!string_to_sid(&map.sid, strlen(GROUP_PREFIX) + (const char *)key.dptr)) {
		DEBUG(0,("Bad sid key '%s' during upgrade\n", (const char *)key.dptr));
		*(int *)state = -1;
		return -1;
	}

	ret = tdb_unpack(data.dptr, data.dsize, "ddff",
			 &map.gid, &map.sid_name_use, &map.nt_name, &map.comment);
	if (ret == -1) {
		DEBUG(0,("Failed to unpack group map record during upgrade\n"));
		*(int *)state = -1;
		return -1;
	}

	if ((int)map.gid == -1) {
		/*
		 * Ignore old invalid mappings
		 */
		return 0;
	}

	if (!add_mapping_entry(&map, 0)) {
		DEBUG(0,("Failed to add mapping entry during upgrade\n"));
		*(int *)state = -1;
		return -1;
	}

	return 0;
}
static bool dbrec2map(const struct db_record *rec, GROUP_MAP *map)
{
	TDB_DATA key = dbwrap_record_get_key(rec);
	TDB_DATA value = dbwrap_record_get_value(rec);
	int ret = 0;
	fstring nt_name;
	fstring comment;

	if ((key.dsize < strlen(GROUP_PREFIX))
	    || (strncmp((char *)key.dptr, GROUP_PREFIX,
			GROUP_PREFIX_LEN) != 0)) {
		return False;
	}

	if (!string_to_sid(&map->sid, (const char *)key.dptr
			   + GROUP_PREFIX_LEN)) {
		return False;
	}

	ret = tdb_unpack(value.dptr, value.dsize, "ddff",
			  &map->gid, &map->sid_name_use,
			  &nt_name, &comment);

	if (ret == -1) {
		DEBUG(3, ("dbrec2map: tdb_unpack failure\n"));
		return false;
	}

	map->nt_name = talloc_strdup(map, nt_name);
	if (!map->nt_name) {
		return false;
	}
	map->comment = talloc_strdup(map, comment);
	if (!map->comment) {
		return false;
	}

	return true;
}
示例#9
0
static int printer_list_exec_fn(struct db_record *rec, void *private_data)
{
    struct printer_list_exec_state *state =
        (struct printer_list_exec_state *)private_data;
    uint32_t time_h, time_l;
    char *name;
    char *comment;
    char *location;
    int ret;
    TDB_DATA key;
    TDB_DATA value;

    key = dbwrap_record_get_key(rec);

    /* always skip PL_TIMESTAMP_KEY key */
    if (strequal((const char *)key.dptr, PL_TIMESTAMP_KEY)) {
        return 0;
    }

    value = dbwrap_record_get_value(rec);

    ret = tdb_unpack(value.dptr, value.dsize,
                     PL_DATA_FORMAT, &time_h, &time_l, &name, &comment,
                     &location);
    if (ret == -1) {
        DEBUG(1, ("Failed to un pack printer data"));
        state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
        return -1;
    }

    state->fn(name, comment, location, state->private_data);

    SAFE_FREE(name);
    SAFE_FREE(comment);
    SAFE_FREE(location);
    return 0;
}
示例#10
0
NTSTATUS printer_list_get_last_refresh(time_t *last_refresh)
{
    struct db_context *db;
    TDB_DATA data;
    uint32_t time_h, time_l;
    NTSTATUS status;
    int ret;

    db = get_printer_list_db();
    if (db == NULL) {
        return NT_STATUS_INTERNAL_DB_CORRUPTION;
    }

    ZERO_STRUCT(data);

    data = dbwrap_fetch_bystring(db, talloc_tos(), PL_TIMESTAMP_KEY);
    if (data.dptr == NULL) {
        DEBUG(1, ("Failed to fetch record!\n"));
        status = NT_STATUS_NOT_FOUND;
        goto done;
    }

    ret = tdb_unpack(data.dptr, data.dsize,
                     PL_TSTAMP_FORMAT, &time_h, &time_l);
    if (ret == -1) {
        DEBUG(1, ("Failed to un pack printer data"));
        status = NT_STATUS_INTERNAL_DB_CORRUPTION;
        goto done;
    }

    *last_refresh = (time_t)(((uint64_t)time_h << 32) + time_l);
    status = NT_STATUS_OK;

done:
    return status;
}
示例#11
0
NTSTATUS printer_list_get_printer(TALLOC_CTX *mem_ctx,
                                  const char *name,
                                  const char **comment,
                                  const char **location,
                                  time_t *last_refresh)
{
    struct db_context *db;
    char *key;
    TDB_DATA data;
    uint32_t time_h, time_l;
    char *nstr = NULL;
    char *cstr = NULL;
    char *lstr = NULL;
    NTSTATUS status;
    int ret;

    db = get_printer_list_db();
    if (db == NULL) {
        return NT_STATUS_INTERNAL_DB_CORRUPTION;
    }

    key = talloc_asprintf(mem_ctx, PL_KEY_FORMAT, name);
    if (!key) {
        DEBUG(0, ("Failed to allocate key name!\n"));
        return NT_STATUS_NO_MEMORY;
    }

    status = dbwrap_fetch_bystring_upper(db, key, key, &data);
    if (!NT_STATUS_IS_OK(status)) {
        DEBUG(1, ("Failed to fetch record!\n"));
        goto done;
    }

    ret = tdb_unpack(data.dptr, data.dsize,
                     PL_DATA_FORMAT,
                     &time_h, &time_l, &nstr, &cstr, &lstr);
    if (ret == -1) {
        DEBUG(1, ("Failed to un pack printer data"));
        status = NT_STATUS_INTERNAL_DB_CORRUPTION;
        goto done;
    }

    if (last_refresh) {
        *last_refresh = (time_t)(((uint64_t)time_h << 32) + time_l);
    }

    if (comment) {
        *comment = talloc_strdup(mem_ctx, cstr);
        if (!*comment) {
            DEBUG(1, ("Failed to strdup comment!\n"));
            status = NT_STATUS_NO_MEMORY;
            goto done;
        }
    }

    if (location) {
        *location = talloc_strdup(mem_ctx, lstr);
        if (*location == NULL) {
            DEBUG(1, ("Failed to strdup location!\n"));
            status = NT_STATUS_NO_MEMORY;
            goto done;
        }
    }

    status = NT_STATUS_OK;

done:
    SAFE_FREE(nstr);
    SAFE_FREE(cstr);
    TALLOC_FREE(key);
    return status;
}
示例#12
0
文件: eventlog.c 项目: gojdic/samba
static bool make_way_for_eventlogs( TDB_CONTEXT * the_tdb, int32_t needed,
				    bool whack_by_date )
{
	int32_t start_record, i, new_start;
	int32_t end_record;
	int32_t reclen, tresv1, trecnum, timegen, timewr;
	int nbytes, len, Retention, MaxSize;
	TDB_DATA key, ret;
	time_t current_time, exp_time;

	/* discard some eventlogs */

	/* read eventlogs from oldest_entry -- there can't be any discontinuity in recnos,
	   although records not necessarily guaranteed to have successive times */
	/* */

	/* lock */
	tdb_lock_bystring_with_timeout( the_tdb, EVT_NEXT_RECORD, 1 );
	/* read */
	end_record = tdb_fetch_int32( the_tdb, EVT_NEXT_RECORD );
	start_record = tdb_fetch_int32( the_tdb, EVT_OLDEST_ENTRY );
	Retention = tdb_fetch_int32( the_tdb, EVT_RETENTION );
	MaxSize = tdb_fetch_int32( the_tdb, EVT_MAXSIZE );

	time( &current_time );

	/* calculate ... */
	exp_time = current_time - Retention;	/* discard older than exp_time */

	/* todo - check for sanity in next_record */
	nbytes = 0;

	DEBUG( 3,
	       ( "MaxSize [%d] Retention [%d] Current Time [%u]  exp_time [%u]\n",
		 MaxSize, Retention, (unsigned int)current_time, (unsigned int)exp_time ) );
	DEBUG( 3,
	       ( "Start Record [%u] End Record [%u]\n",
		(unsigned int)start_record,
		(unsigned int)end_record ));

	for ( i = start_record; i < end_record; i++ ) {
		/* read a record, add the amt to nbytes */
		key.dsize = sizeof(int32_t);
		key.dptr = (unsigned char *)&i;
		ret = tdb_fetch( the_tdb, key );
		if ( ret.dsize == 0 ) {
			DEBUG( 8,
			       ( "Can't find a record for the key, record [%d]\n",
				 i ) );
			tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
			return False;
		}
		nbytes += ret.dsize;	/* note this includes overhead */

		len = tdb_unpack( ret.dptr, ret.dsize, "ddddd", &reclen,
				  &tresv1, &trecnum, &timegen, &timewr );
		if (len == -1) {
			DEBUG( 10,("make_way_for_eventlogs: tdb_unpack failed.\n"));
			tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
			SAFE_FREE( ret.dptr );
			return False;
		}

		DEBUG( 8,
		       ( "read record %u, record size is [%d], total so far [%d]\n",
			 (unsigned int)i, reclen, nbytes ) );

		SAFE_FREE( ret.dptr );

		/* note that other servers may just stop writing records when the size limit
		   is reached, and there are no records older than 'retention'. This doesn't
		   like a very useful thing to do, so instead we whack (as in sleeps with the
		   fishes) just enough records to fit the what we need.  This behavior could
		   be changed to 'match', if the need arises. */

		if ( !whack_by_date && ( nbytes >= needed ) )
			break;	/* done */
		if ( whack_by_date && ( timegen >= exp_time ) )
			break;	/* done */
	}

	DEBUG( 3,
	       ( "nbytes [%d] needed [%d] start_record is [%u], should be set to [%u]\n",
		 nbytes, needed, (unsigned int)start_record, (unsigned int)i ) );
	/* todo - remove eventlog entries here and set starting record to start_record... */
	new_start = i;
	if ( start_record != new_start ) {
		for ( i = start_record; i < new_start; i++ ) {
			key.dsize = sizeof(int32_t);
			key.dptr = (unsigned char *)&i;
			tdb_delete( the_tdb, key );
		}

		tdb_store_int32( the_tdb, EVT_OLDEST_ENTRY, new_start );
	}
	tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
	return True;
}
示例#13
0
/****************************************************************************
 Open the group mapping tdb.
****************************************************************************/
NTSTATUS samba3_read_grouptdb(const char *file, TALLOC_CTX *ctx, struct samba3_groupdb *db)
{
	int32_t vers_id;
	TDB_DATA kbuf, dbuf, newkey;
	int ret;
	TDB_CONTEXT *tdb; 

	tdb = tdb_open(file, 0, TDB_DEFAULT, O_RDONLY, 0600);
	if (!tdb) {
		DEBUG(0,("Failed to open group mapping database\n"));
		return NT_STATUS_UNSUCCESSFUL;
	}

	/* Cope with byte-reversed older versions of the db. */
	vers_id = tdb_fetch_int32(tdb, "INFO/version");
	if ((vers_id == DATABASE_VERSION_V1) || (IREV(vers_id) == DATABASE_VERSION_V1)) {
		/* Written on a bigendian machine with old fetch_int code. Save as le. */
		vers_id = DATABASE_VERSION_V2;
	}

	if (vers_id != DATABASE_VERSION_V2) {
		DEBUG(0, ("Group database version mismatch: %d\n", vers_id));
		return NT_STATUS_UNSUCCESSFUL;
	}

	db->groupmappings = NULL;
	db->groupmap_count = 0;
	db->aliases = NULL;
	db->alias_count = 0;

	for (kbuf = tdb_firstkey(tdb); 
	     kbuf.dptr; 
	     newkey = tdb_nextkey(tdb, kbuf), free(kbuf.dptr), kbuf=newkey) {
		struct samba3_groupmapping map;
		const char *k = (const char *)kbuf.dptr;

		if (strncmp(k, GROUP_PREFIX, strlen(GROUP_PREFIX)) == 0)
		{
			dbuf = tdb_fetch(tdb, kbuf);
			if (!dbuf.dptr)
				continue;

			ZERO_STRUCT(map);

			map.sid = dom_sid_parse_talloc(ctx, k+strlen(GROUP_PREFIX));

			ret = tdb_unpack(tdb, (char *)dbuf.dptr, dbuf.dsize, "dd",
							 &map.gid, &map.sid_name_use);
			
			if ( ret == -1 ) {
				DEBUG(3,("enum_group_mapping: tdb_unpack failure\n"));
				continue;
			}

			map.nt_name = talloc_strdup(ctx, (const char *)(dbuf.dptr+ret));
			map.comment = talloc_strdup(ctx, (const char *)(dbuf.dptr+ret+strlen(map.nt_name)));

			db->groupmappings = talloc_realloc(ctx, db->groupmappings, struct samba3_groupmapping, db->groupmap_count+1);

			if (!db->groupmappings) 
				return NT_STATUS_NO_MEMORY;

			db->groupmappings[db->groupmap_count] = map;

			db->groupmap_count++;
		} else if (strncmp(k, MEMBEROF_PREFIX, strlen(MEMBEROF_PREFIX)) == 0)
示例#14
0
static int regdb_upgrade_v2_to_v3_fn(struct db_record *rec, void *private_data)
{
	const char *keyname;
	fstring subkeyname;
	NTSTATUS status;
	WERROR werr;
	uint8_t *buf;
	uint32_t buflen, len;
	uint32_t num_items;
	uint32_t i;

	if (rec->key.dptr == NULL || rec->key.dsize == 0) {
		return 0;
	}

	keyname = (const char *)rec->key.dptr;

	if (strncmp(keyname, REG_SORTED_SUBKEYS_PREFIX,
		    strlen(REG_SORTED_SUBKEYS_PREFIX)) == 0)
	{
		/* Delete the deprecated sorted subkeys cache. */

		DEBUG(10, ("regdb_upgrade_v2_to_v3: deleting [%s]\n", keyname));

		status = rec->delete_rec(rec);
		if (!NT_STATUS_IS_OK(status)) {
			DEBUG(0, ("regdb_upgrade_v2_to_v3: tdb_delete for [%s] "
				  "failed!\n", keyname));
			return 1;
		}

		return 0;
	}

	if (strncmp(keyname, REG_VALUE_PREFIX, strlen(REG_VALUE_PREFIX)) == 0) {
		DEBUG(10, ("regdb_upgrade_v2_to_v3: skipping [%s]\n", keyname));
		return 0;
	}

	if (strncmp(keyname, REG_SECDESC_PREFIX,
		    strlen(REG_SECDESC_PREFIX)) == 0)
	{
		DEBUG(10, ("regdb_upgrade_v2_to_v3: skipping [%s]\n", keyname));
		return 0;
	}

	/*
	 * Found a regular subkey list record.
	 * Walk the list and create the list record for those
	 * subkeys that don't already have one.
	 */
	DEBUG(10, ("regdb_upgrade_v2_to_v3: scanning subkey list of [%s]\n",
		   keyname));

	buf = rec->value.dptr;
	buflen = rec->value.dsize;

	len = tdb_unpack(buf, buflen, "d", &num_items);
	if (len == (uint32_t)-1) {
		/* invalid or empty - skip */
		return 0;
	}

	for (i=0; i<num_items; i++) {
		len += tdb_unpack(buf+len, buflen-len, "f", subkeyname);
		DEBUG(10, ("regdb_upgrade_v2_to_v3: "
			   "writing subkey list for [%s\\%s]\n",
			   keyname, subkeyname));
		werr = regdb_store_subkey_list(regdb, keyname, subkeyname);
		if (!W_ERROR_IS_OK(werr)) {
			return 1;
		}
	}

	return 0;
}
示例#15
0
文件: registry.c 项目: sangfo/WMI_cmd
NTSTATUS samba3_read_regdb ( const char *fn, TALLOC_CTX *ctx, struct samba3_regdb *db )
{
    uint32_t vers_id;
    TDB_CONTEXT *tdb;
    TDB_DATA kbuf, vbuf;

    /* placeholder tdb; reinit upon startup */

    if ( !(tdb = tdb_open(fn, 0, TDB_DEFAULT, O_RDONLY, 0600)) )
    {
        DEBUG(0, ("Unable to open registry database %s\n", fn));
        return NT_STATUS_UNSUCCESSFUL;
    }

    vers_id = tdb_fetch_int32(tdb, "INFO/version");

    db->key_count = 0;
    db->keys = NULL;

    if (vers_id != -1 && vers_id >= REGVER_V1) {
        DEBUG(0, ("Registry version mismatch: %d\n", vers_id));
        return NT_STATUS_UNSUCCESSFUL;
    }

    for (kbuf = tdb_firstkey(tdb); kbuf.dptr; kbuf = tdb_nextkey(tdb, kbuf))
    {
        uint32_t len;
        int i;
        struct samba3_regkey key;
        char *skey;

        if (strncmp((char *)kbuf.dptr, VALUE_PREFIX, strlen(VALUE_PREFIX)) == 0)
            continue;

        vbuf = tdb_fetch(tdb, kbuf);

        key.name = talloc_strdup(ctx, (char *)kbuf.dptr);

        len = tdb_unpack(tdb, (char *)vbuf.dptr, vbuf.dsize, "d", &key.subkey_count);

        key.value_count = 0;
        key.values = NULL;
        key.subkeys = talloc_array(ctx, char *, key.subkey_count);

        for (i = 0; i < key.subkey_count; i++) {
            fstring tmp;
            len += tdb_unpack( tdb, (char *)vbuf.dptr+len, vbuf.dsize-len, "f", tmp );
            key.subkeys[i] = talloc_strdup(ctx, tmp);
        }

        skey = talloc_asprintf(ctx, "%s/%s", VALUE_PREFIX, kbuf.dptr );

        vbuf = tdb_fetch_bystring( tdb, skey );

        if ( vbuf.dptr ) {
            regdb_unpack_values( tdb, ctx, &key, vbuf );
        }

        db->keys = talloc_realloc(ctx, db->keys, struct samba3_regkey, db->key_count+1);
        db->keys[db->key_count] = key;
        db->key_count++;
    }

    tdb_close(tdb);

    return NT_STATUS_OK;
}