Index: trunk/Nebulous-Server/lib/Nebulous/Server.pm
===================================================================
--- trunk/Nebulous-Server/lib/Nebulous/Server.pm	(revision 13278)
+++ trunk/Nebulous-Server/lib/Nebulous/Server.pm	(revision 13302)
@@ -1,5 +1,5 @@
 # Copyright (c) 2004  Joshua Hoblitt
 #
-# $Id: Server.pm,v 1.40 2007-05-05 02:59:31 jhoblitt Exp $
+# $Id: Server.pm,v 1.41 2007-05-08 02:27:42 jhoblitt Exp $
 
 package Nebulous::Server;
@@ -13,4 +13,5 @@
 
 use DBI;
+use File::ExtAttr qw( setfattr );
 use Log::Log4perl;
 use Nebulous::Server::Config;
@@ -124,5 +125,5 @@
     my $self = shift;
 
-    my ($key, $volume) = validate_pos(@_,
+    my ($key, $vol_name) = validate_pos(@_,
         {
             type        => SCALAR,
@@ -143,15 +144,4 @@
 
     $log->debug( "entered - @_" );
-
-    # Find the storage volume to use.  We are doing this first as there's no
-    # point in creating a new storage object if we can't allocate a new
-    # instance. ->_get_storage_volume() throws an exception on failure.
-    my ($vol_id, $vol_path);
-    eval {
-        ($vol_id, $vol_path) = $self->_get_storage_volume($volume);
-    };
-    if ($@) {
-        $log->logdie($@);
-    }
 
     my $ins_id;
@@ -206,13 +196,9 @@
     # actual on disk file name we can't try to create the file until after
     # we've create both a new storage_storage object and instance.
-    my $filename = $key;
-    # mange '/'s into ':'
-    $filename =~ s|/|:|g;
-    my $uri = URI::file->new("$vol_path/$ins_id.$filename");
-    $log->debug("generated uri $uri");
 
     # TODO add some stuff here to retry if unsucessful
-    eval {
-        _create_empty_file($uri->file, $key);
+    my ($uri, $vol_id);
+    eval {
+        ($uri, $vol_id) = $self->_create_empty_instance_file($key, $ins_id, $vol_name);
     };
     if ($@) {
@@ -220,4 +206,6 @@
         $log->logdie($@);
     }
+
+    $log->debug("created $uri on volume ID: $vol_id");
 
     eval {
@@ -225,8 +213,9 @@
             my $query = $db->prepare_cached( $sql->update_instance_uri );
             # vol_id, uri, ins_id
-            $query->execute($vol_id, $uri, $ins_id);
+            $query->execute($vol_id, "$uri", $ins_id);
         }
 
         $db->commit;
+        $log->debug("commit");
     };
     if ($@) {
@@ -315,12 +304,4 @@
 
     $log->debug("entered - @_");
-
-    my ($vol_id, $vol_path);
-    eval {
-        ($vol_id, $vol_path) = $self->_get_storage_volume($vol_name);
-    };
-    if ($@) {
-        $log->logdie($@);
-    }
 
     my $ins_id;
@@ -361,13 +342,12 @@
     }
 
-    my $filename = $key;
-    # mange '/'s into ':'
-    $filename =~ s|/|:|g;
-    my $uri = URI::file->new("$vol_path/$ins_id.$filename");
-    $log->debug("generated uri $uri");
+    # Unfortunately, since we want to use the instance row's ID as part of the
+    # actual on disk file name we can't try to create the file until after
+    # we've create both a new storage_storage object and instance.
 
     # TODO add some stuff here to retry if unsucessful
-    eval {
-        _create_empty_file($uri->file, $key);
+    my ($uri, $vol_id);
+    eval {
+        ($uri, $vol_id) = $self->_create_empty_instance_file($key, $ins_id, $vol_name);
     };
     if ($@) {
@@ -1107,5 +1087,5 @@
     my $name = shift;
 
-    my ($vol_id, $vol_path);
+    my ($vol_id, $vol_path, $xattr);
     eval {
         # TODO cache this?
@@ -1137,5 +1117,5 @@
 
         my $free;
-        ($vol_id, $vol_path, $free) = $query->fetchrow_array;
+        ($vol_id, $vol_path, $xattr, $free) = $query->fetchrow_array;
         $query->finish;
     };
@@ -1150,5 +1130,5 @@
     $log->debug( "leaving" );
 
-    return ($vol_id, $vol_path);
+    return ($vol_id, $vol_path, $xattr);
 }
 
@@ -1210,4 +1190,77 @@
 }
 
+sub _create_empty_instance_file
+{
+    my $self = shift;
+
+    my ($key, $ins_id, $volume) = @_;
+
+    my $log = $self->log;
+    my $sql = $self->sql;
+    my $db  =$self->db;
+
+    # Find the storage volume to use. 
+    # ->_get_storage_volume() throws an exception on failure.
+    my ($vol_id, $vol_path, $xattr);
+    my ($storage_filename);
+    my ($uri);
+    eval {
+        ($vol_id, $vol_path, $xattr) = $self->_get_storage_volume($volume);
+        $storage_filename = $self->_generate_storage_filename($key, $ins_id);
+        # XXX put a hook for directory hashing here
+        $uri = URI::file->new("$vol_path/$storage_filename");
+        $log->debug("generated uri $uri");
+        $self->_create_empty_file($uri->file);
+    };
+    if ($@) {
+        $log->logdie($@);
+    }
+
+    if ($xattr) {
+        my $path = $uri->file;
+        die "can not set xattr on $path: $!"
+            unless (setfattr($path, 'user.nebulous_key', $key));
+    }
+
+    return ($uri, $vol_id);
+}
+
+
+sub _create_empty_file
+{
+    my $self = shift;
+
+    my ($path) = @_;
+
+    # perl's open() can't do an O_CREAT | O_EXCL
+    die "file $path already exists" if (-e $path);
+
+    my $fh;
+    die "can not open $path: $!"
+        unless (open($fh, '>', $path));
+
+    die "can not close $path: $!"
+        unless (close($fh));
+
+    die "can not chmod $path: $!"
+        unless (chmod 0664, $path);
+
+    return $path;
+}
+
+
+sub _generate_storage_filename
+{
+    my $self = shift;
+
+    my ($key, $ins_id) = @_;
+
+    my $filename = $key;
+    # mangle '/'s into ':'
+    $filename =~ s|/|:|g;
+
+    return "$ins_id.$filename"
+}
+
 
 sub DESTROY
