/* Hash a filename for a directory lookup. The string is hashed to a 64-bit
* value. It is guaranteed that the hash values 0, 1, and 2 are never returned
* (to allow the hash value to be used as an NFS cookie for a READDIR
- * operation). */
+ * operation). TODO: We perhaps should make this a keyed hash, and may want to
+ * use something cheaper than MD5 to compute. */
uint64_t bluesky_directory_hash(gchar *name)
{
GChecksum *csum = g_checksum_new(G_CHECKSUM_MD5);
g_free(dirent);
}
-static gint bluesky_dirent_compare(gconstpointer a, gconstpointer b,
- gpointer unused)
+gint bluesky_dirent_compare(gconstpointer a, gconstpointer b,
+ gpointer unused)
{
/* We can't simply subtract the hash values, since they are 64-bit and the
* result could overflow when converted to a gint. */
BlueSkyDirent d = {name, hash, 0};
GSequenceIter *i = g_sequence_search(inode->dirents, &d,
bluesky_dirent_compare, NULL);
+
+ /* g_sequence_search will return an iterator pointing just beyond the
+ * desired node if there is a match, so that when inserting the new node
+ * will go after the match. But we are trying to do a lookup, so back up
+ * by one position. */
i = g_sequence_iter_prev(i);
if (g_sequence_iter_is_end(i))
return 0;
BlueSkyDirent *dirent = g_sequence_get(i);
- g_print("Lookup(%s) -> 0x%016llx\n", name, dirent->hash);
if (dirent->hash != hash)
return 0;
if (g_strcmp0(name, dirent->name) != 0)
return 0;
+ g_print("Lookup(%s) -> 0x%016llx\n", name, dirent->hash);
return dirent->inum;
}
uint64_t hash = bluesky_directory_hash(name);
BlueSkyDirent *d = g_new(BlueSkyDirent, 1);
- d->name = name;
+ d->name = g_strdup(name);
d->hash = hash;
d->inum = inum;
GSequenceIter *i = g_sequence_search(dir->dirents, d,
bluesky_dirent_compare, NULL);
+
+ /* If a directory entry already exists, return failure. The caller must
+ * delete the old entry and try again. TODO: We'll fail on a hash
+ * collision; we should handle that case. */
+ if (!g_sequence_iter_is_end(g_sequence_iter_prev(i))) {
+ BlueSkyDirent *d2 = g_sequence_get(g_sequence_iter_prev(i));
+ if (d2->hash == hash)
+ return FALSE;
+ }
+
g_sequence_insert_before(i, d);
+ dir->change_count++;
return TRUE;
}