This is a WIP description of the nscd database file format. updates to this could describe what each element of the individual structures mean, mention each record format, etc.

The nscd database file has a binary sparse format and consists of a header a hash table and a data area. The header is described in nscd-client.h as database_pers_head. It's structure (and the structures of all the records/headers mentioned here) as of glibc-2.18 is documented here for reference, but is subject to change:

   1 /* Header of persistent database file.  */
   2 struct database_pers_head
   3 {
   4   int32_t version;
   5   int32_t header_size;
   6   volatile int32_t gc_cycle;
   7   volatile int32_t nscd_certainly_running;
   8   volatile nscd_time_t timestamp;
   9   /* Room for extensions.  */
  10   volatile uint32_t extra_data[4];
  11 
  12   nscd_ssize_t module;
  13   nscd_ssize_t data_size;
  14 
  15   nscd_ssize_t first_free;      /* Offset of first free byte in data area.  */
  16 
  17   nscd_ssize_t nentries;
  18   nscd_ssize_t maxnentries;
  19   nscd_ssize_t maxnsearched;
  20 
  21   uint64_t poshit;
  22   uint64_t neghit;
  23   uint64_t posmiss;
  24   uint64_t negmiss;
  25 
  26   uint64_t rdlockdelayed;
  27   uint64_t wrlockdelayed;
  28 
  29   uint64_t addfailed;
  30 
  31   ref_t array[0];
  32 };

The hash table is situated right after the header and can be accessed using the array member of the header. The size of the hash table is fixed at database_pers_head.module entries. Each entry in the table is an offset to a hashentry object from the end of the hash table. A hashentry has the following format:

   1 /* Structure for one hash table entry.  */
   2 struct hashentry
   3 {
   4   request_type type:8;          /* Which type of dataset.  */
   5   bool first;                   /* True if this was the original key.  */
   6   nscd_ssize_t len;             /* Length of key.  */
   7   ref_t key;                    /* Pointer to key.  */
   8   int32_t owner;                /* If secure table, this is the owner.  */
   9   ref_t next;                   /* Next entry in this hash bucket list.  */
  10   ref_t packet;                 /* Records for the result.  */
  11   union
  12   {
  13     struct hashentry *dellist;  /* Next record to be deleted.  This can be a
  14                                    pointer since only nscd uses this field.  */
  15     ref_t *prevp;               /* Pointer to field containing forward
  16                                    reference.  */
  17   };
  18 };

This is also defined in nscd-client.h. The hash function used to compute the offset into the hash table may compute the same value for two different hash entries. These entries are placed in a linked list with the newest entry referenced directly by the offset in the hash table and further entries by the offset in the next element of the hashentry.

A hashentry further points to the record that contains the actual results. The format of this record is different for different types of results. The type of result is recorded in the type element of the hashenry, which is an enumerator of request_type and can have the following values:

   1 /* Available services.  */
   2 typedef enum
   3 {
   4   GETPWBYNAME,
   5   GETPWBYUID,
   6   GETGRBYNAME,
   7   GETGRBYGID,
   8   GETHOSTBYNAME,
   9   GETHOSTBYNAMEv6,
  10   GETHOSTBYADDR,
  11   GETHOSTBYADDRv6,
  12   SHUTDOWN,             /* Shut the server down.  */
  13   GETSTAT,              /* Get the server statistic.  */
  14   INVALIDATE,           /* Invalidate one special cache.  */
  15   GETFDPW,
  16   GETFDGR,
  17   GETFDHST,
  18   GETAI,
  19   INITGROUPS,
  20   GETSERVBYNAME,
  21   GETSERVBYPORT,
  22   GETFDSERV,
  23   GETNETGRENT,
  24   INNETGR,
  25   GETFDNETGR,
  26   LASTREQ
  27 } request_type;

Each record also has a key, which is an offset to a location where the key string is stored. len defines the length of the key. The record pointed to by this hasnentry is located at an offset (from the end of the hash table) stored in packet. One may read the contents by looking at the structure of the record for that specific type. For example, the record for the INITGROUPS request is:

   1 /* Structure sent in reply to initgroups query.  Note that this struct is
   2    sent also if the service is disabled or there is no record found.  */
   3 typedef struct
   4 {
   5   int32_t version;
   6   int32_t found;
   7   nscd_ssize_t ngrps;
   8 } initgr_response_header;