/* // Full copyright information is available in the file ../doc/CREDITS // // Interface to dbm index of object locations. */ #include "defs.h" #include <sys/types.h> #ifdef __UNIX__ #include <sys/file.h> #endif #include <sys/stat.h> #ifdef __UNIX__ #include <ndbm.h> #else #include "ndbm.h" #endif #include <fcntl.h> #include <string.h> #include "cdc_db.h" #include "util.h" #ifdef S_IRUSR #define READ_WRITE (S_IRUSR | S_IWUSR) #define READ_WRITE_EXECUTE (S_IRUSR | S_IWUSR | S_IXUSR) #else #define READ_WRITE 0600 #define READ_WRITE_EXECUTE 0700 #endif INTERNAL datum objnum_key(Long objnum, Number_buf nbuf); INTERNAL datum name_key(Long name); INTERNAL datum offset_size_value(off_t offset, Int size, Number_buf nbuf); INTERNAL void parse_offset_size_value(datum value, off_t *offset, Int *size); INTERNAL datum objnum_value(Long objnum, Number_buf nbuf); INTERNAL void sync_name_cache(void); INTERNAL Int store_name(Long name, Long objnum); INTERNAL Int get_name(Long name, Long *objnum); INTERNAL DBM *dbp; struct name_cache_entry { Long name; Long objnum; char dirty; char on_disk; } name_cache[NAME_CACHE_SIZE + 1]; void lookup_open(char *name, Int cnew) { Int i; if (cnew) dbp = dbm_open(name, O_TRUNC | O_RDWR | O_CREAT | O_BINARY, READ_WRITE); else dbp = dbm_open(name, O_RDWR | O_BINARY, READ_WRITE); if (!dbp) fail_to_start("Cannot open dbm database file."); for (i = 0; i < NAME_CACHE_SIZE; i++) name_cache[i].name = NOT_AN_IDENT; } void lookup_close(void) { sync_name_cache(); dbm_close(dbp); } void lookup_sync(void) { char buf[255]; sprintf(buf, "%s/index", c_dir_binary); /* Only way to do this with ndbm is close and re-open. */ sync_name_cache(); dbm_close(dbp); dbp = dbm_open(buf, O_RDWR | O_CREAT | O_BINARY, READ_WRITE); if (!dbp) panic("Cannot reopen dbm database file."); } Int lookup_retrieve_objnum(Long objnum, off_t *offset, Int *size) { datum key, value; Number_buf nbuf; /* Get the value for objnum from the database. */ key = objnum_key(objnum, nbuf); value = dbm_fetch(dbp, key); if (!value.dptr) return 0; parse_offset_size_value(value, offset, size); return 1; } Int lookup_store_objnum(Long objnum, off_t offset, Int size) { datum key, value; Number_buf nbuf1, nbuf2; key = objnum_key(objnum, nbuf1); value = offset_size_value(offset, size, nbuf2); if (dbm_store(dbp, key, value, DBM_REPLACE)) { write_err("ERROR: Failed to store key %l.", objnum); return 0; } return 1; } Int lookup_remove_objnum(Long objnum) { datum key; Number_buf nbuf; /* Remove the key from the database. */ key = objnum_key(objnum, nbuf); if (dbm_delete(dbp, key)) { write_err("ERROR: Failed to delete key %l.", objnum); return 0; } return 1; } Long lookup_first_objnum(void) { datum key; key = dbm_firstkey(dbp); if (key.dptr == NULL) return INV_OBJNUM; if (key.dsize > 1 && *key.dptr == 0) return atoln(key.dptr + 1, key.dsize - 1); return lookup_next_objnum(); } Long lookup_next_objnum(void) { datum key; key = dbm_nextkey(dbp); if (key.dptr == NULL) return NOT_AN_IDENT; if (key.dsize > 1 && *key.dptr == 0) return atoln(key.dptr + 1, key.dsize - 1); return lookup_next_objnum(); } Int lookup_retrieve_name(Long name, Long *objnum) { Int i = name % NAME_CACHE_SIZE; /* See if it's in the cache. */ if (name_cache[i].name == name) { *objnum = name_cache[i].objnum; return 1; } /* Get it from the database. */ if (!get_name(name, objnum)) return 0; /* Discard the old cache entry if it exists. */ if (name_cache[i].name != NOT_AN_IDENT) { if (name_cache[i].dirty) store_name(name_cache[i].name, name_cache[i].objnum); ident_discard(name_cache[i].name); } /* Make a new cache entry. */ name_cache[i].name = ident_dup(name); name_cache[i].objnum = *objnum; name_cache[i].dirty = 0; name_cache[i].on_disk = 1; return 1; } Int lookup_store_name(Long name, Long objnum) { Int i = name % NAME_CACHE_SIZE; /* See if it's in the cache. */ if (name_cache[i].name == name) { if (name_cache[i].objnum != objnum) { name_cache[i].objnum = objnum; name_cache[i].dirty = 1; } return 1; } /* Discard the old cache entry if it exists. */ if (name_cache[i].name != NOT_AN_IDENT) { if (name_cache[i].dirty) store_name(name_cache[i].name, name_cache[i].objnum); ident_discard(name_cache[i].name); } /* Make a new cache entry. */ name_cache[i].name = ident_dup(name); name_cache[i].objnum = objnum; name_cache[i].dirty = 1; name_cache[i].on_disk = 0; return 1; } Int lookup_remove_name(Long name) { datum key; Int i = name % NAME_CACHE_SIZE; /* See if it's in the cache. */ if (name_cache[i].name == name) { /* Delete it from the cache. If it's not on disk, then we're done. */ /*write_err("##lookup_remove_name %d %s", name_cache[i].name, ident_name(name_cache[i].name));*/ ident_discard(name_cache[i].name); name_cache[i].name = NOT_AN_IDENT; if (!name_cache[i].on_disk) return 1; } /* Remove the key from the database. */ key = name_key(name); if (dbm_delete(dbp, key)) return 0; return 1; } Long lookup_first_name(void) { datum key; sync_name_cache(); key = dbm_firstkey(dbp); if (key.dptr == NULL) return NOT_AN_IDENT; if (key.dsize == 1 || *key.dptr != 0) return ident_get(key.dptr); return lookup_next_name(); } Long lookup_next_name(void) { datum key; key = dbm_nextkey(dbp); if (key.dptr == NULL) return NOT_AN_IDENT; if (key.dsize == 1 || *key.dptr != 0) return ident_get(key.dptr); return lookup_next_name(); } INTERNAL datum objnum_key(Long objnum, Number_buf nbuf) { char *s; datum key; /* Set up a key for a objnum. The first byte will be 0, distinguishing it * from a string. */ s = long_to_ascii(objnum, nbuf); #if DISABLED *--s = 0; key.dptr = s; key.dsize = strlen(s + 1) + 2; #else key.dptr = s-1; key.dsize = strlen(s) + 2; #endif return key; } INTERNAL datum offset_size_value(off_t offset, Int size, Number_buf nbuf) { char *s; Number_buf tmp_buf; datum value; /* Set up a value for the offset and size. */ s = long_to_ascii(offset, tmp_buf); nbuf[0] = 0; strcpy(nbuf, s); strcat(nbuf, ";"); s = long_to_ascii(size, tmp_buf); strcat(nbuf, s); value.dptr = nbuf; value.dsize = strlen(nbuf) + 1; return value; } INTERNAL void parse_offset_size_value(datum value, off_t *offset, Int *size) { char *p; *offset = atol(value.dptr); p = strchr(value.dptr, ';'); *size = atol(p + 1); } INTERNAL datum name_key(Long name) { datum key; /* Set up a key for the name. Include the 0 byte at the end. */ key.dptr = ident_name(name); key.dsize = strlen(key.dptr) + 1; return key; } INTERNAL datum objnum_value(Long objnum, Number_buf nbuf) { char *s; datum value; s = long_to_ascii(objnum, nbuf); value.dptr = s; value.dsize = strlen(s) + 1; return value; } INTERNAL void sync_name_cache(void) { Int i; for (i = 0; i < NAME_CACHE_SIZE; i++) { if (name_cache[i].name != NOT_AN_IDENT && name_cache[i].dirty) { store_name(name_cache[i].name, name_cache[i].objnum); name_cache[i].dirty = 0; name_cache[i].on_disk = 1; } } } INTERNAL Int store_name(Long name, Long objnum) { datum key, value; Number_buf nbuf; /* Set up the value structure. */ value = objnum_value(objnum, nbuf); key = name_key(name); if (dbm_store(dbp, key, value, DBM_REPLACE)) { write_err("ERROR: Failed to store key %s.", name); return 0; } return 1; } INTERNAL Int get_name(Long name, Long *objnum) { datum key, value; /* Get the key from the database. */ key = name_key(name); value = dbm_fetch(dbp, key); if (!value.dptr) return 0; *objnum = atol(value.dptr); return 1; }