root/lang/perl/Fuse-Hatena/trunk/lib/Fuse/Hatena/Base.pm @ 3171

Revision 3171, 8.3 kB (checked in by typester, 7 years ago)

時刻周り、権限周り実装強化。emacsでもちゃんと編集できるようになったはず

Line 
1package Fuse::Hatena::Base;
2use strict;
3use warnings;
4use base qw/Class::Accessor::Fast/;
5
6__PACKAGE__->mk_accessors(qw/mountpoint username password ua files/);
7
8use Class::C3;
9
10use Fuse;
11use Fuse::Hatena::UserAgent;
12
13use Errno qw/:POSIX/;
14use Fcntl qw/:DEFAULT :mode/;
15
16sub new {
17    my $self = shift->SUPER::new( @_ > 1 ? {@_} : $_[0] );
18
19    $self->{ua} ||= Fuse::Hatena::UserAgent->new;
20
21    $self->{files} = {};
22    $self->{attrs} = {};
23
24    $self;
25}
26
27sub mount {
28    my $self = shift;
29
30    $self->{mounttime} = time;
31
32    my $bind = sub {
33        my $sub = shift;
34        my $method = $self->can($sub) ? $sub : sub { -ENOSYS() };
35
36        sub { $self->$method(@_) };
37    };
38
39    Fuse::main(
40        mountpoint => $self->{mountpoint},
41        $self->{debug} ? (debug => 1) : (),
42        map { $_ => $bind->("fuse_$_") }
43            qw/getattr readlink getdir mknod mkdir unlink rmdir symlink rename link
44               chmod chown truncate utime open read write statfs/,
45    );
46}
47
48sub find {
49    my ($self, $path) = @_;
50
51    my $file = $self->{files};
52    my @path = grep {$_} split '/', $path;
53
54    while (my $p = shift @path) {
55        if (@path) {
56            $file = $file->{$p};
57            return unless defined $file and ref $file eq 'HASH';
58        }
59        else {
60            return $file->{$p};
61        }
62    }
63
64    return $self->{files};
65}
66
67sub default_attr {
68    my ($self, $path) = @_;
69
70    my $file  = $self->find($path);
71    my $mtime = $self->{mounttime} ||= time;
72
73    my $mode
74        = ref($file) eq 'HASH'   ? S_IFDIR | 0755
75        : ref($file) eq 'SCALAR' ? S_IFLNK | 0777
76        : ref($file) eq ''       ? S_IFREG | 0644
77        :                          S_IFREG | 0000;
78
79    $mode = S_IFREG | 0200 unless defined $file;
80
81    my $size = ref($file) ? 1 : defined($file) ? length($file) : 0;
82
83    return(
84        0, 0, $mode, 1, $>, $)+0, 0, $size, $mtime, $mtime, $mtime, 1024, 1
85    );
86}
87
88sub attr {
89    my ($self, $path, $attrs) = @_;
90
91    my %keys = do {
92        my $i = 0;
93        map { $_ => $i++ } qw/dev ino mode nlink uid gud rdev
94            size atime mtime ctime blksize blocks/;
95    };
96
97    my $attr = $self->{attrs}{$path} || [ $self->default_attr($path) ];
98    if ($attrs) {
99        while (my ($k,$v) = each %$attrs) {
100            $attr->[ $keys{$k} ] = $v;
101        }
102    }
103
104    @$attr;
105}
106
107sub fuse_getattr {
108    my ($self, $path) = @_;
109    my $file = $self->find($path);
110    return -ENOENT() unless defined $file;
111
112    $self->attr($path);
113}
114
115sub fuse_getdir {
116    my ($self, $path) = @_;
117    my $dir = $self->find($path);
118
119    return -ENOENT() unless defined $dir;
120    return -ENOENT() unless ref $dir eq 'HASH';
121
122    (qw/. ../, sort(keys %$dir), 0);
123}
124
125sub fuse_open {
126    my ($self, $path, $mode) = @_;
127    my $file = $self->find($path);
128
129#    return -EBADF() unless defined($file) or ($mode & O_ACCMODE());
130    return ref $file eq 'HASH' ? -EISDIR() : ref $file eq '' ? 0 : -ENOSYS();
131}
132
133sub fuse_read {
134    my ($self, $path, $size, $offset) = @_;
135
136    my $file = $self->find($path);
137
138    return -ENOENT() unless defined $file;
139    return -ENOENT() if ref $file;
140
141    $self->attr($path => { atime => time });
142
143    if ($offset > length $file) {
144        return -EINVAL();
145    }
146    elsif ($offset == length $file) {
147        return 0;
148    }
149    else {
150        return substr($file, $offset, $size);
151    }
152}
153
154sub fuse_write {
155    my ($self, $path, $buf, $offset) = @_;
156
157    my $file = $self->find($path);
158    return -ENOENT() unless defined $file;
159    return -ENOSYS() unless ref $file eq '';
160#    return -ENOSYS() unless 書き込み権限ないとき
161
162    $file = $self->{files};
163    my @path = grep {$_} split '/', $path;
164
165    while (my $p = shift @path) {
166        if (@path) {
167            $file = $file->{$p};
168            return -ENOENT() unless defined $file and ref $file eq 'HASH';
169        }
170        else {
171            return -EINVAL() unless ref $file->{$p} eq '';
172
173            if ($offset > length($file->{$p})) {
174                $file->{$p} .= "\0" x ($offset - length $file->{$p});
175            }
176            substr($file->{$p}, $offset) = $buf;
177        }
178    }
179    $self->attr($path => { mtime => time });
180
181    length($buf);
182}
183
184sub fuse_readlink {
185    my ($self, $path) = @_;
186
187    my $file = $self->find($path);
188    return -EINVAL() unless ref $file eq 'SCALAR';
189
190    $$file;
191}
192
193sub fuse_unlink {
194    my ($self, $path) = @_;
195
196    my $file = $self->{files};
197    my @path = grep {$_} split '/', $path;
198
199    while (my $p = shift @path) {
200        if (@path) {
201            $file = $file->{$p};
202            return -ENOENT() unless defined $file and ref $file eq 'HASH';
203        }
204        else {
205            delete $file->{$p};
206            delete $self->{attrs}{$path};
207            return 0;
208        }
209    }
210
211    -ENOENT();
212}
213
214sub fuse_symlink {
215    my ($self, $target, $path) = @_;
216
217    my $file = $self->{files};
218    my @path = grep {$_} split '/', $path;
219
220    while (my $p = shift @path) {
221        if (@path) {
222            $file = $file->{$p};
223            return -ENOENT() unless defined $file and ref $file eq 'HASH';
224        }
225        else {
226            if (exists $file->{$p}) {
227                return -EEXIST();
228            }
229            else {
230                $file->{$p} = \$target;
231                return 0;
232            }
233        }
234    }
235
236    -ENOENT();
237}
238
239sub fuse_rename {
240    my ($self, $old, $new) = @_;
241
242    return -EEXIST() if $self->find($new);
243
244    my $file = $self->{files};
245    my @path = grep {$_} split '/', $new;
246
247    while (my $p = shift @path) {
248        if (@path) {
249            $file = $file->{$p};
250        }
251        else {
252            delete $self->{attrs}{$old};
253            $old = delete $file->{$p};
254        }
255    }
256
257    $file = $self->{files};
258    @path = grep {$_} split '/', $new;
259
260    while (my $p = shift @path) {
261        if (@path) {
262            $file = $file->{$p};
263            return -ENOENT() unless defined $file and ref $file eq 'HASH';
264        }
265        else {
266            $file->{$p} = $old;
267            return 0;
268        }
269    }
270
271    -ENOENT();
272}
273
274sub fuse_chmod {
275    my ($self, $path, $mode) = @_;
276
277    my $file = $self->find($path);
278    return -ENOENT() unless defined $file;
279
280    $mode
281        = ref($file) eq 'HASH'   ? S_IFDIR | $mode
282        : ref($file) eq 'SCALAR' ? S_IFLNK | $mode
283        :                          S_IFREG | $mode;
284
285    $self->attr($path, { mode => $mode });
286
287    0;
288}
289
290sub fuse_truncate {
291    my ($self, $path, $offset) = @_;
292
293    my $file = $self->{files};
294    my @path = grep {$_} split '/', $path;
295
296    while (my $p = shift @path) {
297        if (@path) {
298            $file = $file->{$p};
299            return -ENOENT() unless defined $file and ref $file eq 'HASH';
300        }
301        else {
302            return -ENOENT() unless ref $file->{$p} eq '';
303            $file->{$p} = substr($file->{$p}, 0, $offset);
304            return 0;
305        }
306    }
307
308    -ENOSYS();
309}
310
311sub fuse_utime {
312    my ($self, $path, $atime, $mtime) = @_;
313
314    $self->attr($path => { atime => $atime, mtime => $mtime });
315
316    0;
317}
318
319sub fuse_mkdir {
320    my ($self, $path, $mode) = @_;
321
322    my $file = $self->{files};
323    my @path = grep {$_} split '/', $path;
324
325    while (my $p = shift @path) {
326        if (@path) {
327            $file = $file->{$p};
328            return -ENOENT() unless defined $file and ref $file eq 'HASH';
329        }
330        else {
331            return -EEXIST() if defined $file->{$p};
332
333            $file->{$p} = {};
334            $self->attr( $path => { mode => S_IFDIR | $mode } );
335            return 0;
336        }
337    }
338
339    -ENOENT();
340}
341
342sub fuse_rmdir {
343    my ($self, $path) = @_;
344
345    my $file = $self->{files};
346    my @path = grep {$_} split '/', $path;
347
348    while (my $p = shift @path) {
349        if (@path) {
350            $file = $file->{$p};
351            return -ENOENT() unless defined $file and ref $file eq 'HASH';
352        }
353        else {
354            delete $file->{$p};
355            delete $self->{attrs}{$path};
356            return 0;
357        }
358    }
359
360    -ENOSYS();
361}
362
363sub fuse_mknod {
364    my ($self, $path) = @_;
365
366    my $file = $self->{files};
367    my @path = grep {$_} split '/', $path;
368
369    while (my $p = shift @path) {
370        if (@path) {
371            $file = $file->{$p};
372            return -ENOENT() unless defined $file and ref $file eq 'HASH';
373        }
374        else {
375            return -EEXIST() if defined $file->{$p};
376            $file->{$p} = '';
377            return 0;
378        }
379    }
380
381    -ENOENT();
382}
383
384sub fuse_statfs { 255, 1000000, 500000, 1000000, 500000, 4096 }
385
386sub fuse_release {0}
387
388sub fuse_flush {0}
389
3901;
391
Note: See TracBrowser for help on using the browser.