Index: /lang/perl/Fuse-Hatena/trunk/lib/Fuse/Hatena/Base.pm
===================================================================
--- /lang/perl/Fuse-Hatena/trunk/lib/Fuse/Hatena/Base.pm (revision 3155)
+++ /lang/perl/Fuse-Hatena/trunk/lib/Fuse/Hatena/Base.pm (revision 3171)
@@ -42,6 +42,5 @@
         map { $_ => $bind->("fuse_$_") }
             qw/getattr readlink getdir mknod mkdir unlink rmdir symlink rename link
-               chmod chown truncate utime open read write statfs flush release fsync
-               setxattr getxattr listxattr removexattr/,
+               chmod chown truncate utime open read write statfs/,
     );
 }
@@ -50,12 +49,11 @@
     my ($self, $path) = @_;
 
-    my $file = $self->files;
-    my @path = grep {$_} split '/', $path;
-
-    return $file unless @path;
-
-    while (my $p = shift @path) {
-        if (@path) {
-            $file = $file->{$p};
+    my $file = $self->{files};
+    my @path = grep {$_} split '/', $path;
+
+    while (my $p = shift @path) {
+        if (@path) {
+            $file = $file->{$p};
+            return unless defined $file and ref $file eq 'HASH';
         }
         else {
@@ -64,61 +62,71 @@
     }
 
-    -ENOENT();
+    return $self->{files};
 }
 
 sub default_attr {
-    my ($self, $file) = @_;
+    my ($self, $path) = @_;
+
+    my $file  = $self->find($path);
+    my $mtime = $self->{mounttime} ||= time;
 
     my $mode
         = ref($file) eq 'HASH'   ? S_IFDIR | 0755
         : ref($file) eq 'SCALAR' ? S_IFLNK | 0777
-        :                          S_IFREG | 0644;
-
-    return (
-        0, 0,    # $dev, $ino,
-        $mode,
-        1,       # $nlink, see fuse.sourceforge.net/wiki/index.php/FAQ
-        $>, $) + 0,    # $uid, $gid,
-        0,             # $rdev,
-        ref($file) ? 1 : length $file || 0,    # $size,
-        $self->{mounttime}, $self->{mounttime},
-        $self->{mounttime},               # actually $atime, $mtime, $ctime,
-        1024, 1,                          # $blksize, $blocks,
+        : ref($file) eq ''       ? S_IFREG | 0644
+        :                          S_IFREG | 0000;
+
+    $mode = S_IFREG | 0200 unless defined $file;
+
+    my $size = ref($file) ? 1 : defined($file) ? length($file) : 0;
+
+    return(
+        0, 0, $mode, 1, $>, $)+0, 0, $size, $mtime, $mtime, $mtime, 1024, 1
     );
 }
 
+sub attr {
+    my ($self, $path, $attrs) = @_;
+
+    my %keys = do {
+        my $i = 0;
+        map { $_ => $i++ } qw/dev ino mode nlink uid gud rdev
+            size atime mtime ctime blksize blocks/;
+    };
+
+    my $attr = $self->{attrs}{$path} || [ $self->default_attr($path) ];
+    if ($attrs) {
+        while (my ($k,$v) = each %$attrs) {
+            $attr->[ $keys{$k} ] = $v;
+        }
+    }
+
+    @$attr;
+}
+
 sub fuse_getattr {
     my ($self, $path) = @_;
-
     my $file = $self->find($path);
     return -ENOENT() unless defined $file;
 
-    my $attr = $self->{attrs}{$path};
-    $attr ? @$attr : $self->default_attr($file);
+    $self->attr($path);
 }
 
 sub fuse_getdir {
     my ($self, $path) = @_;
-
-    my $file = $self->find($path);
-    return -ENOSYS() unless defined $file;
-    return -ENOENT() unless ref $file eq 'HASH';
-
-    (qw/. ../, sort( keys %$file ), 0);
+    my $dir = $self->find($path);
+
+    return -ENOENT() unless defined $dir;
+    return -ENOENT() unless ref $dir eq 'HASH';
+
+    (qw/. ../, sort(keys %$dir), 0);
 }
 
 sub fuse_open {
-    my ($self, $path, $flags) = @_;
-
-    my $file = $self->find($path);
-    return -ENOSYS() unless defined $file;
-#    return -EBADF() unless $flags & O_ACCMODE;
-
-    if (ref($file) eq 'HASH') {
-        return -EISDIR();
-    }
-    else {
-        return 0;
-    }
+    my ($self, $path, $mode) = @_;
+    my $file = $self->find($path);
+
+#    return -EBADF() unless defined($file) or ($mode & O_ACCMODE());
+    return ref $file eq 'HASH' ? -EISDIR() : ref $file eq '' ? 0 : -ENOSYS();
 }
 
@@ -127,7 +135,10 @@
 
     my $file = $self->find($path);
+
     return -ENOENT() unless defined $file;
     return -ENOENT() if ref $file;
 
+    $self->attr($path => { atime => time });
+
     if ($offset > length $file) {
         return -EINVAL();
@@ -141,67 +152,41 @@
 }
 
-sub fuse_release { 0 }
-
-sub fuse_flush { 0 }
-
-sub fuse_utime { 0 }
-
 sub fuse_write {
     my ($self, $path, $buf, $offset) = @_;
 
-    my $date;
-    my $file = $self->{files};
-
-    my @p = grep { $_ } split '/', $path;
-    while (my $p = shift @p) {
-        if (@p) {
-            $file = $file->{$p};
-        }
-        else {
-            my $len = length $file->{$p};
-            if ($offset > $len) {
-                $file->{$p} .= "\0" x ($offset - $len);
+    my $file = $self->find($path);
+    return -ENOENT() unless defined $file;
+    return -ENOSYS() unless ref $file eq '';
+#    return -ENOSYS() unless 書き込み権限ないとき
+
+    $file = $self->{files};
+    my @path = grep {$_} split '/', $path;
+
+    while (my $p = shift @path) {
+        if (@path) {
+            $file = $file->{$p};
+            return -ENOENT() unless defined $file and ref $file eq 'HASH';
+        }
+        else {
+            return -EINVAL() unless ref $file->{$p} eq '';
+
+            if ($offset > length($file->{$p})) {
+                $file->{$p} .= "\0" x ($offset - length $file->{$p});
             }
-            substr( $file->{$p}, $offset ) = $buf;
-        }
-    }
-
-    length $buf;
-}
-
-sub fuse_mknod {
-    my ($self, $path) = @_;
-
-    my $file = $self->{files};
-    for my $p (split '/', $path) {
-        next if $p eq '';
-
-        unless (exists $file->{$p}) {
-            $file->{$p} = '';
-            return 0;
-        }
-
-        $file = $file->{$p};
-    }
-
-    -EEXIST();
-}
-
-sub fuse_mkdir {
-    my ($self, $path) = @_;
-
-    my $file = $self->{files};
-    for my $p (split '/', $path) {
-        next if $p eq '';
-
-        unless (exists $file->{$p}) {
-            $file->{$p} = {};
-            return 0;
-        }
-
-        $file = $file->{$p};
-    }
-
-    -EEXIST();
+            substr($file->{$p}, $offset) = $buf;
+        }
+    }
+    $self->attr($path => { mtime => time });
+
+    length($buf);
+}
+
+sub fuse_readlink {
+    my ($self, $path) = @_;
+
+    my $file = $self->find($path);
+    return -EINVAL() unless ref $file eq 'SCALAR';
+
+    $$file;
 }
 
@@ -209,33 +194,75 @@
     my ($self, $path) = @_;
 
-    my $date;
-    my $file = $self->{files};
-
-    my @p = grep { $_ } split '/', $path;
-    while (my $p = shift @p) {
-        $date .= $p;
-        if (@p) {
-            $file = $file->{$p};
+    my $file = $self->{files};
+    my @path = grep {$_} split '/', $path;
+
+    while (my $p = shift @path) {
+        if (@path) {
+            $file = $file->{$p};
+            return -ENOENT() unless defined $file and ref $file eq 'HASH';
         }
         else {
             delete $file->{$p};
-            return 0;
-        }
-    }
-
-    -ENOENT();
-}
-
-sub fuse_rmdir {
-    my ($self, $path) = @_;
-
-    my $file = $self->{files};
-    my @p = grep { $_ } split '/', $path;
-    while (my $p = shift @p) {
-        if (@p) {
-            $file = $file->{$p};
-        }
-        else {
-            delete $file->{$p};
+            delete $self->{attrs}{$path};
+            return 0;
+        }
+    }
+
+    -ENOENT();
+}
+
+sub fuse_symlink {
+    my ($self, $target, $path) = @_;
+
+    my $file = $self->{files};
+    my @path = grep {$_} split '/', $path;
+
+    while (my $p = shift @path) {
+        if (@path) {
+            $file = $file->{$p};
+            return -ENOENT() unless defined $file and ref $file eq 'HASH';
+        }
+        else {
+            if (exists $file->{$p}) {
+                return -EEXIST();
+            }
+            else {
+                $file->{$p} = \$target;
+                return 0;
+            }
+        }
+    }
+
+    -ENOENT();
+}
+
+sub fuse_rename {
+    my ($self, $old, $new) = @_;
+
+    return -EEXIST() if $self->find($new);
+
+    my $file = $self->{files};
+    my @path = grep {$_} split '/', $new;
+
+    while (my $p = shift @path) {
+        if (@path) {
+            $file = $file->{$p};
+        }
+        else {
+            delete $self->{attrs}{$old};
+            $old = delete $file->{$p};
+        }
+    }
+
+    $file = $self->{files};
+    @path = grep {$_} split '/', $new;
+
+    while (my $p = shift @path) {
+        if (@path) {
+            $file = $file->{$p};
+            return -ENOENT() unless defined $file and ref $file eq 'HASH';
+        }
+        else {
+            $file->{$p} = $old;
             return 0;
         }
@@ -248,41 +275,116 @@
     my ($self, $path, $mode) = @_;
 
-    my $attr = $self->{attrs}{$path} || [$self->default_attr];
-    $attr->[2] = $mode;
-
-    $self->{attrs}{$path} = $attr;
+    my $file = $self->find($path);
+    return -ENOENT() unless defined $file;
+
+    $mode
+        = ref($file) eq 'HASH'   ? S_IFDIR | $mode
+        : ref($file) eq 'SCALAR' ? S_IFLNK | $mode
+        :                          S_IFREG | $mode;
+
+    $self->attr($path, { mode => $mode });
 
     0;
 }
 
-sub fuse_readlink {
-    my ($self, $path) = @_;
-
-    my $file = $self->find($path);
-    return -ENOENT() unless defined $file;
-    return -EINVAL() unless ref $file eq 'SCALAR';
-
-    $$file;
-}
-
-sub fuse_symlink {
-    my ($self, $target, $path) = @_;
-
-    my $file = $self->{files};
-    for my $p (split '/', $path) {
-        next if $p eq '';
-
-        unless (exists $file->{$p}) {
-            $file->{$p} = \$target;
-            return 0;
-        }
-
-        $file = $file->{$p};
-    }
-
-    -EEXIST();
-}
-
-sub fuse_statfs { (255,1,1,1,1,2) }
+sub fuse_truncate {
+    my ($self, $path, $offset) = @_;
+
+    my $file = $self->{files};
+    my @path = grep {$_} split '/', $path;
+
+    while (my $p = shift @path) {
+        if (@path) {
+            $file = $file->{$p};
+            return -ENOENT() unless defined $file and ref $file eq 'HASH';
+        }
+        else {
+            return -ENOENT() unless ref $file->{$p} eq '';
+            $file->{$p} = substr($file->{$p}, 0, $offset);
+            return 0;
+        }
+    }
+
+    -ENOSYS();
+}
+
+sub fuse_utime {
+    my ($self, $path, $atime, $mtime) = @_;
+
+    $self->attr($path => { atime => $atime, mtime => $mtime });
+
+    0;
+}
+
+sub fuse_mkdir {
+    my ($self, $path, $mode) = @_;
+
+    my $file = $self->{files};
+    my @path = grep {$_} split '/', $path;
+
+    while (my $p = shift @path) {
+        if (@path) {
+            $file = $file->{$p};
+            return -ENOENT() unless defined $file and ref $file eq 'HASH';
+        }
+        else {
+            return -EEXIST() if defined $file->{$p};
+
+            $file->{$p} = {};
+            $self->attr( $path => { mode => S_IFDIR | $mode } );
+            return 0;
+        }
+    }
+
+    -ENOENT();
+}
+
+sub fuse_rmdir {
+    my ($self, $path) = @_;
+
+    my $file = $self->{files};
+    my @path = grep {$_} split '/', $path;
+
+    while (my $p = shift @path) {
+        if (@path) {
+            $file = $file->{$p};
+            return -ENOENT() unless defined $file and ref $file eq 'HASH';
+        }
+        else {
+            delete $file->{$p};
+            delete $self->{attrs}{$path};
+            return 0;
+        }
+    }
+
+    -ENOSYS();
+}
+
+sub fuse_mknod {
+    my ($self, $path) = @_;
+
+    my $file = $self->{files};
+    my @path = grep {$_} split '/', $path;
+
+    while (my $p = shift @path) {
+        if (@path) {
+            $file = $file->{$p};
+            return -ENOENT() unless defined $file and ref $file eq 'HASH';
+        }
+        else {
+            return -EEXIST() if defined $file->{$p};
+            $file->{$p} = '';
+            return 0;
+        }
+    }
+
+    -ENOENT();
+}
+
+sub fuse_statfs { 255, 1000000, 500000, 1000000, 500000, 4096 }
+
+sub fuse_release {0}
+
+sub fuse_flush {0}
 
 1;
