static int write_db_file(const char *reason) { Objid oid; Objid max_oid = db_last_used_objid(); Verbdef *v; Var user_list; int i; volatile int nprogs = 0; volatile int success = 1; for (oid = 0; oid <= max_oid; oid++) { if (valid(oid)) for (v = dbpriv_find_object(oid)->verbdefs; v; v = v->next) if (v->program) nprogs++; } user_list = db_all_users(); TRY { dbio_printf(header_format_string, current_db_version); dbio_printf("%d\n%d\n%d\n%d\n", max_oid + 1, nprogs, 0, user_list.v.list[0].v.num); for (i = 1; i <= user_list.v.list[0].v.num; i++) dbio_write_objid(user_list.v.list[i].v.obj); oklog("%s: Writing %d objects...\n", reason, max_oid + 1); for (oid = 0; oid <= max_oid; oid++) { write_object(oid); if (oid == max_oid || log_report_progress()) oklog("%s: Done writing %d objects...\n", reason, oid + 1); } oklog("%s: Writing %d MOO verb programs...\n", reason, nprogs); for (i = 0, oid = 0; oid <= max_oid; oid++) if (valid(oid)) { int vcount = 0; for (v = dbpriv_find_object(oid)->verbdefs; v; v = v->next) { if (v->program) { dbio_printf("#%d:%d\n", oid, vcount); dbio_write_program(v->program); if (++i == nprogs || log_report_progress()) oklog("%s: Done writing %d verb programs...\n", reason, i); } vcount++; } } oklog("%s: Writing forked and suspended tasks...\n", reason); write_task_queue(); oklog("%s: Writing list of formerly active connections...\n", reason); write_active_connections(); } EXCEPT(dbpriv_dbio_failed) success = 0; ENDTRY; return success; }
int dbpriv_count_properties(Objid oid) { Object *o; int nprops = 0; for (o = dbpriv_find_object(oid); o; o = dbpriv_find_object(o->parent)) nprops += o->propdefs.cur_length; return nprops; }
static void insert_prop(Objid oid, int pos, Pval pval) { Pval *new_propval; Object *o; int i, nprops; nprops = dbpriv_count_properties(oid); new_propval = mymalloc(nprops * sizeof(Pval), M_PVAL); o = dbpriv_find_object(oid); for (i = 0; i < pos; i++) new_propval[i] = o->propval[i]; new_propval[pos] = pval; new_propval[pos].var = var_ref(pval.var); if (new_propval[pos].perms & PF_CHOWN) new_propval[pos].owner = o->owner; for (i = pos + 1; i < nprops; i++) new_propval[i] = o->propval[i - 1]; if (o->propval) myfree(o->propval, M_PVAL); o->propval = new_propval; }
static void insert_prop_recursively(Objid root, int root_pos, Pval pv) { Objid c; insert_prop(root, root_pos, pv); pv.var.type = TYPE_CLEAR; /* do after initial insert_prop so only children will be TYPE_CLEAR */ for (c = dbpriv_find_object(root)->child; c != NOTHING; c = dbpriv_find_object(c)->sibling) { int new_prop_count = dbpriv_find_object(c)->propdefs.cur_length; insert_prop_recursively(c, new_prop_count + root_pos, pv); } }
static int property_defined_at_or_below(const char *pname, int phash, Objid oid) { /* Return true iff some descendant of OID defines a property named PNAME. */ Objid c; Proplist *props = &dbpriv_find_object(oid)->propdefs; int length = props->cur_length; int i; for (i = 0; i < length; i++) if (props->l[i].hash == phash && !mystrcasecmp(props->l[i].name, pname)) return 1; for (c = dbpriv_find_object(oid)->child; c != NOTHING; c = dbpriv_find_object(c)->sibling) if (property_defined_at_or_below(pname, phash, c)) return 1; return 0; }
void db_destroy_object(Objid oid) { Object *o = dbpriv_find_object(oid); Verbdef *v, *w; int i; db_priv_affected_callable_verb_lookup(); if (!o) panic("DB_DESTROY_OBJECT: Invalid object!"); if (o->location != NOTHING || o->contents != NOTHING || o->parent != NOTHING || o->child != NOTHING) panic("DB_DESTROY_OBJECT: Not a barren orphan!"); if (is_user(oid)) { Var t; t.type = TYPE_OBJ; t.v.obj = oid; all_users = setremove(all_users, t); } free_str(o->name); for (i = 0; i < o->propdefs.cur_length; i++) { /* As an orphan, the only properties on this object are the ones * defined on it directly, so these two arrays must be the same length. */ free_str(o->propdefs.l[i].name); free_var(o->propval[i].var); } if (o->propval) myfree(o->propval, M_PVAL); if (o->propdefs.l) myfree(o->propdefs.l, M_PROPDEF); for (v = o->verbdefs; v; v = w) { if (v->program) free_program(v->program); free_str(v->name); w = v->next; myfree(v, M_VERBDEF); } myfree(objects[oid], M_OBJECT); objects[oid] = 0; }
int dbpriv_count_properties(Objid oid) { Var ancestor, ancestors; int i, c, nprops = 0; Object *o; if (NOTHING == oid) return 0; ancestors = db_ancestors(oid, true); FOR_EACH(ancestor, ancestors, i, c) { o = dbpriv_find_object(ancestor.v.obj); nprops += o->propdefs.cur_length; }
static void write_object(Objid oid) { Object *o; Verbdef *v; int i; int nverbdefs, nprops; if (!valid(oid)) { dbio_printf("#%d recycled\n", oid); return; } o = dbpriv_find_object(oid); dbio_printf("#%d\n", oid); dbio_write_string(o->name); dbio_write_string(""); /* placeholder for old handles string */ dbio_write_num(o->flags); dbio_write_objid(o->owner); dbio_write_objid(o->location); dbio_write_objid(o->contents); dbio_write_objid(o->next); dbio_write_objid(o->parent); dbio_write_objid(o->child); dbio_write_objid(o->sibling); for (v = o->verbdefs, nverbdefs = 0; v; v = v->next) nverbdefs++; dbio_write_num(nverbdefs); for (v = o->verbdefs; v; v = v->next) write_verbdef(v); dbio_write_num(o->propdefs.cur_length); for (i = 0; i < o->propdefs.cur_length; i++) write_propdef(&o->propdefs.l[i]); nprops = dbpriv_count_properties(oid); dbio_write_num(nprops); for (i = 0; i < nprops; i++) write_propval(o->propval + i); }
int db_add_propdef(Objid oid, const char *pname, Var value, Objid owner, unsigned flags) { Object *o; Pval pval; int i; db_prop_handle h; h = db_find_property(oid, pname, 0); if (h.ptr || property_defined_at_or_below(pname, str_hash(pname), oid)) return 0; o = dbpriv_find_object(oid); if (o->propdefs.cur_length == o->propdefs.max_length) { Propdef *old_props = o->propdefs.l; int new_size = (o->propdefs.max_length == 0 ? 8 : 2 * o->propdefs.max_length); o->propdefs.l = mymalloc(new_size * sizeof(Propdef), M_PROPDEF); for (i = 0; i < o->propdefs.max_length; i++) o->propdefs.l[i] = old_props[i]; o->propdefs.max_length = new_size; if (old_props) myfree(old_props, M_PROPDEF); } o->propdefs.l[o->propdefs.cur_length++] = dbpriv_new_propdef(pname); pval.var = value; pval.owner = owner; pval.perms = flags; insert_prop_recursively(oid, o->propdefs.cur_length - 1, pval); return 1; }
} o->propdefs.l[o->propdefs.cur_length++] = dbpriv_new_propdef(pname); pval.var = value; pval.owner = owner; pval.perms = flags; insert_prop_recursively(oid, o->propdefs.cur_length - 1, pval); return 1; } int db_rename_propdef(Objid oid, const char *old, const char *new) { Proplist *props = &dbpriv_find_object(oid)->propdefs; int hash = str_hash(old); int count = props->cur_length; int i; db_prop_handle h; for (i = 0; i < count; i++) { Propdef p; p = props->l[i]; if (p.hash == hash && !mystrcasecmp(p.name, old)) { if (mystrcasecmp(old, new) != 0) { /* Not changing just the case */ h = db_find_property(oid, new, 0); if (h.ptr || property_defined_at_or_below(new, str_hash(new), oid)) return 0;
static int validate_hierarchies() { Objid oid, log_oid; Objid size = db_last_used_objid() + 1; int broken = 0; int fixed_nexts = 0; oklog("VALIDATING the object hierarchies ...\n"); # define PROGRESS_INTERVAL 10000 # define MAYBE_LOG_PROGRESS \ { \ if (oid == log_oid) { \ log_oid += PROGRESS_INTERVAL; \ oklog("VALIDATE: Done through #%d ...\n", oid); \ } \ } oklog("VALIDATE: Phase 1: Check for invalid objects ...\n"); for (oid = 0, log_oid = PROGRESS_INTERVAL; oid < size; oid++) { Object *o = dbpriv_find_object(oid); MAYBE_LOG_PROGRESS; if (o) { if (o->location == NOTHING && o->next != NOTHING) { o->next = NOTHING; fixed_nexts++; } # define CHECK(field, name) \ { \ if (o->field != NOTHING \ && !dbpriv_find_object(o->field)) { \ errlog("VALIDATE: #%d.%s = #%d <invalid> ... fixed.\n", \ oid, name, o->field); \ o->field = NOTHING; \ } \ } CHECK(parent, "parent"); CHECK(child, "child"); CHECK(sibling, "sibling"); CHECK(location, "location"); CHECK(contents, "contents"); CHECK(next, "next"); # undef CHECK } } if (fixed_nexts != 0) errlog("VALIDATE: Fixed %d should-be-null next pointer(s) ...\n", fixed_nexts); /* * The next two phases are only done if the moo is launched with -r * and can be used with DBs that have broken hierarchies. */ if (recovery_mode) { oklog("EMERGENCY REBUILD PHASE 1: Removing old contents and child lists ...\n"); for (oid = 0, log_oid = PROGRESS_INTERVAL; oid < size; oid++) { Object *o = dbpriv_find_object(oid); MAYBE_LOG_PROGRESS; if (o) { o->contents = NOTHING; o->next = NOTHING; o->child = NOTHING; o->sibling = NOTHING; } } oklog("EMERGENCY REBUILD PHASE 2: Rebuilding contents and child lists ...\n"); for (oid = 0, log_oid = PROGRESS_INTERVAL; oid < size; oid++) { Object *o = dbpriv_find_object(oid); MAYBE_LOG_PROGRESS; if (o) { /* find this obj's parent & loc */ Objid parent = o->parent; Objid location = o->location; if (parent != NOTHING) { Object *po = dbpriv_find_object(parent); Objid lastchild = po->lastchild; if (lastchild != NOTHING) { Object *co = dbpriv_find_object(lastchild); co->sibling = oid; po->lastchild = oid; } else { po->child = oid; po->lastchild = oid; } } if (location != NOTHING) { Object *lo = dbpriv_find_object(location); Objid lastcontents = lo->lastcontents; if (lastcontents != NOTHING) { Object *co = dbpriv_find_object(lastcontents); co->next = oid; lo->lastcontents = oid; } else { lo->contents = oid; lo->lastcontents = oid; } } } /* endif o */ } /* for oid */ } /* recovery_mode */ oklog("VALIDATE: Phase 2: Check for cycles ...\n"); for (oid = 0, log_oid = PROGRESS_INTERVAL; oid < size; oid++) { Object *o = dbpriv_find_object(oid); Objid list_end; MAYBE_LOG_PROGRESS; if (o) { # define CHECK(start, field, name) \ { \ Objid oid2 = start; \ int count = 0; \ for (; oid2 != NOTHING \ ; oid2 = dbpriv_find_object(oid2)->field) { \ if (++count > size) { \ errlog("VALIDATE: Cycle in `%s' chain of #%d\n",\ name, oid); \ broken = 1; \ break; \ } \ list_end = oid2; \ } \ } CHECK(o->parent, parent, "parent"); list_end = NOTHING; CHECK(o->child, sibling, "child"); o->lastchild = list_end; CHECK(o->location, location, "location"); list_end = NOTHING; CHECK(o->contents, next, "contents"); o->lastcontents = list_end; # undef CHECK } } if (broken) /* Can't continue if cycles found */ return 0; oklog("VALIDATING the object hierarchies ... finished.\n"); return !broken; }
static int validate_hierarchies() { Objid oid; Objid size = db_last_used_objid() + 1; int broken = 0; int fixed_nexts = 0; oklog("VALIDATING the object hierarchies ...\n"); # define MAYBE_LOG_PROGRESS \ { \ if (log_report_progress()) { \ oklog("VALIDATE: Done through #%d ...\n", oid); \ } \ } oklog("VALIDATE: Phase 1: Check for invalid objects ...\n"); for (oid = 0; oid < size; oid++) { Object *o = dbpriv_find_object(oid); MAYBE_LOG_PROGRESS; if (o) { if (o->location == NOTHING && o->next != NOTHING) { o->next = NOTHING; fixed_nexts++; } # define CHECK(field, name) \ { \ if (o->field != NOTHING \ && !dbpriv_find_object(o->field)) { \ errlog("VALIDATE: #%d.%s = #%d <invalid> ... fixed.\n", \ oid, name, o->field); \ o->field = NOTHING; \ } \ } CHECK(parent, "parent"); CHECK(child, "child"); CHECK(sibling, "sibling"); CHECK(location, "location"); CHECK(contents, "contents"); CHECK(next, "next"); # undef CHECK } } if (fixed_nexts != 0) errlog("VALIDATE: Fixed %d should-be-null next pointer(s) ...\n", fixed_nexts); oklog("VALIDATE: Phase 2: Check for cycles ...\n"); for (oid = 0; oid < size; oid++) { Object *o = dbpriv_find_object(oid); MAYBE_LOG_PROGRESS; if (o) { # define CHECK(start, field, name) \ { \ Objid slower = start; \ Objid faster = slower; \ while (faster != NOTHING) { \ faster = dbpriv_find_object(faster)->field; \ if (faster == NOTHING) \ break; \ faster = dbpriv_find_object(faster)->field; \ slower = dbpriv_find_object(slower)->field; \ if (faster == slower) { \ errlog("VALIDATE: Cycle in `%s' chain of #%d\n", \ name, oid); \ broken = 1; \ break; \ } \ } \ } CHECK(o->parent, parent, "parent"); CHECK(o->child, sibling, "child"); CHECK(o->location, location, "location"); CHECK(o->contents, next, "contents"); # undef CHECK /* setup for phase 3: set two temp flags on every object */ o->flags |= (3<<FLAG_FIRST_TEMP); } } if (broken) /* Can't continue if cycles found */ return 0; oklog("VALIDATE: Phase 3a: Finding delusional parents ...\n"); for (oid = 0; oid < size; oid++) { Object *o = dbpriv_find_object(oid); MAYBE_LOG_PROGRESS; if (o) { # define CHECK(up, down, down_name, across, FLAG) \ { \ Objid oidkid; \ Object *okid; \ \ for (oidkid = o->down; \ oidkid != NOTHING; \ oidkid = okid->across) { \ \ okid = dbpriv_find_object(oidkid); \ if (okid->up != oid) { \ errlog( \ "VALIDATE: #%d erroneously on #%d's %s list.\n", \ oidkid, oid, down_name); \ broken = 1; \ } \ else { \ /* mark okid as properly claimed */ \ okid->flags &= ~(1<<(FLAG)); \ } \ } \ } CHECK(parent, child, "child", sibling, FLAG_FIRST_TEMP); CHECK(location, contents, "contents", next, FLAG_FIRST_TEMP+1); # undef CHECK } } oklog("VALIDATE: Phase 3b: Finding delusional children ...\n"); for (oid = 0; oid < size; oid++) { Object *o = dbpriv_find_object(oid); MAYBE_LOG_PROGRESS; if (o) { # define CHECK(up, up_name, down_name, FLAG) \ { \ /* If oid is unclaimed, up must be NOTHING */ \ if ((o->flags & (1<<(FLAG))) && o->up != NOTHING) { \ errlog("VALIDATE: #%d not in %s (#%d)'s %s list.\n", \ oid, up_name, o->up, down_name); \ broken = 1; \ } \ } CHECK(parent, "parent", "child", FLAG_FIRST_TEMP); CHECK(location, "location", "contents", FLAG_FIRST_TEMP+1); /* clear temp flags */ o->flags &= ~(3<<FLAG_FIRST_TEMP); # undef CHECK } } oklog("VALIDATING the object hierarchies ... finished.\n"); return !broken; }
int valid(Objid oid) { return dbpriv_find_object(oid) != 0; }