Hook NFS proxy together with BlueSky core.
[bluesky.git] / dir.c
diff --git a/dir.c b/dir.c
index c4cbec2..eab71be 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -16,7 +16,8 @@
 /* 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);
@@ -73,17 +74,22 @@ uint64_t bluesky_directory_lookup(BlueSkyInode *inode, gchar *name)
     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;
 }
 
@@ -96,13 +102,24 @@ gboolean bluesky_directory_insert(BlueSkyInode *dir, gchar *name, uint64_t 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;
 }