/* $Id: fhdb.C,v 1.6 2001/09/11 03:03:09 fubob Exp $ */ /* * Copyright (C) 1999, 2000 Kevin Fu (fubob@mit.edu) * Copyright (C) 1999 Frans Kaashoek (kaashoek@mit.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ #include "sfsrodb.h" /* rpc_vec sucks. It can't realloc. So we use a vec kludge */ struct fhdb_indir_temp { vec key; vec child; }; /* Given: A vec of file handles Effects: Creates a FHDB block from the handles, then adds the block to the database. Returns: File handle of the block written to the database */ sfs_hash fhdb_dir_add (vec &fhv, char *iv) { sfsro_data dat (SFSRO_FHDB_DIR); dat.fhdb_dir->set (fhv.base (), fhv.size ()); /* fhdb_dir.setsize (fhv.size ()); for (u_int32_t i = 0; i < fhv.size (); i++) { fhdb_dir[i] = fhv [i]; } */ // hash fhv and add to database size_t calllen = 0; char *callbuf = NULL; xdrsuio x (XDR_ENCODE); if (xdr_sfsro_data (x.xdrp (), &dat)) { calllen = x.uio ()->resid (); callbuf = suio_flatten (x.uio ()); } sfs_hash fh; create_sfsrofh (iv, SFSRO_IVSIZE, &fh, callbuf, calllen); if (!sfsrodb_put (sfsrofhdb, fh.base (), fh.size (), callbuf, calllen)) { identical_fhdb++; } else { fhdb_cnt++; } xfree (callbuf); return fh; } /* Given: vecs of keys and children Effects: Pops a key off the back, then creates a FHDB_INDIR block from the vecs, then adds the block to the database. Returns: File handle of the block written to the database */ sfs_hash fhdb_indir_add (fhdb_indir_temp &indir, char *iv) { sfsro_data dat (SFSRO_FHDB_INDIR); assert (indir.key.size () == indir.child.size ()); assert (indir.key.size () > 0); indir.key.pop_back (); // Get rid of unnecessary key dat.fhdb_indir->key.set (indir.key.base (), indir.key.size ()); dat.fhdb_indir->child.set (indir.child.base (), indir.child.size ()); // hash fhv and add to database size_t calllen = 0; char *callbuf = NULL; xdrsuio x (XDR_ENCODE); if (xdr_sfsro_data (x.xdrp (), &dat)) { calllen = x.uio ()->resid (); callbuf = suio_flatten (x.uio ()); } sfs_hash fh; create_sfsrofh (iv, SFSRO_IVSIZE, &fh, callbuf, calllen); if (!sfsrodb_put (sfsrofhdb, fh.base (), fh.size (), callbuf, calllen)) { identical_fhdb++; } else { fhdb_cnt++; } xfree (callbuf); return fh; } /* Given: A path of the hash tree (indirv), a level to insert at, and a key/child to insert Effects: Insert the key/child into indirv[level] Requires: indirv[level] not be already full and that level not more than one+ already existing. */ void fhdb_insert (vec &indirv, sfs_hash &child, sfs_hash &key, u_int16_t level) { assert (indirv.size () >= level); if (indirv.size () == level) { indirv.push_back (); } assert (indirv[level].child.size () < SFSRO_FHDB_CHILDREN); indirv[level].child.push_back (child); indirv[level].key.push_back (key); } /* Given: A vec of indirect nodes, a level Effects: If this level is full, insert into db and propagate hash upwards. Restore the "no full nodes" property If force == true, then it will forcibly insert the partially complete block, etc. and propagate. */ void fhdb_propagate (vec &indirv, u_int16_t level, char *iv) { assert (indirv.size () > level); if (indirv[level].child.size () == SFSRO_FHDB_CHILDREN) { sfs_hash child, key; child = fhdb_indir_add (indirv[level], iv); key = indirv[level].key[0]; indirv[level].key.clear (); indirv[level].child.clear (); fhdb_insert (indirv, child, key, level + 1); // Top of the tree? if (indirv[level + 1].child.size () != 1) fhdb_propagate (indirv, level + 1, iv); } } /* Like propagate, but for finalizer. Requires: No empty indirvs at bottom of tree. At least one indir. */ void fhdb_finalize_propagate (vec &indirv, char *iv) { assert (indirv.size () != 0); // Top of tree with single hash if ((indirv.size () == 1) && (indirv[0].child.size () == 1)) return; // Is it possible to have an inner node with one child? If so, compress that level // upwards? That is, cut out this indir, and embed in parent. sfs_hash child, key; // Is this a singular node? Then just move upwards. if (indirv.front ().child.size () == 1) { assert (indirv[0].child.size () == indirv[0].key.size ()); child = indirv.front ().child[0]; key = indirv.front ().key[0]; } else { child = fhdb_indir_add (indirv.front (), iv); key = indirv.front ().key[0]; } indirv.pop_front (); fhdb_insert (indirv, child, key, 0); fhdb_finalize_propagate (indirv, iv); } /* Given: Vec of indirs Effects: Write to database. Requires: No indirs are full. All must be partially full or empty. */ sfs_hash fhdb_finalize (vec &indirv, char *iv) { // Get rid of empty indirvs at the bottom of tree while (indirv.front ().child.size () == 0) indirv.pop_front (); fhdb_finalize_propagate (indirv, iv); // At this point, the fhdb is the child of the backmost indirv. assert (indirv.back ().child.size () == 1); return (indirv.back ().child[0]); } void fhdb_copydb () { #if 0 #ifdef SLEEPYCAT DBT key, content; DBC *cursor; bzero (&key, sizeof (key)); bzero (&content, sizeof (content)); sfsrofhdb->cursor (sfsrofhdb, NULL, &cursor, 0); while (cursor->c_get (cursor, &key, &content, DB_NEXT) != DB_NOTFOUND) { if (!sfsrodb_put (sfsrodb, key.data, key.size, content.data, content.size)) { #else bIteration *cursor = new bIteration(); record *item; while (sfsrofhdb->iterate(cursor, &item) == 0) { bSize_t keySize, contentSize; void *keyData = item->getKey(&keySize); void *contentData = item->getValue(&contentSize); if (!sfsrodb_put (sfsrodb, keyData, keySize, contentData, contentSize)) { #endif warn << "Found identical fhdb, compressing.\n"; identical_fhdb++; } } #endif } int create_fhdb (sfs_hash *fhdb, const char *dbfile, char *iv) { ref info = dbGetImplInfo(); //create the generic object dbfe* db = new dbfe(); //set up the options we want dbOptions opts; //ideally, we would check the validity of these... opts.addOption("opt_async", 0); opts.addOption("opt_cachesize", 80000); opts.addOption("opt_nodesize", 4096); int err = db->createdb("/tmp/fhdb.db", opts); err = db->opendb("/tmp/fhdb.db", opts); if (err) { warn << "open returned: " << strerror(err) << err << "\n"; exit (1); } vec fhv; vec indirv; sfs_hash fh; #if 0 #ifdef SLEEPYCAT while (cursor->c_get (cursor, &key, &content, DB_NEXT) != DB_NOTFOUND) { memcpy (fh.base (), key.data, key.size); #else record *item; while (sfsrodb->iterate(cursor, &item) == 0) { bSize_t keySize; void *keyData = item->getKey(&keySize); memcpy(fh.base(), keyData, keySize); delete item; #endif fhv.push_back (fh); if (fhv.size () == SFSRO_FHDB_CHILDREN) { fh = fhdb_dir_add (fhv, iv); fhdb_insert (indirv, fh, fhv[0], 0); fhdb_propagate (indirv, 0, iv); fhv.clear (); } } #endif if (fhv.size () > 0) { fh = fhdb_dir_add (fhv, iv); fhdb_insert (indirv, fh, fhv[0], 0); fhdb_propagate (indirv, 0, iv); fhv.clear (); } *fhdb = fhdb_finalize (indirv, iv); fhdb_copydb (); sfsrofhdb->closedb(); if (!fhdb) { warnx << "fhdb null!\n"; return -1; } return 0; }