line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl -w |
2
|
|
|
|
|
|
|
package Fuse::Simple; # in file Fuse/Simple.pm |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
=head1 NAME |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
Fuse::Simple - Simple way to write filesystems in Perl using FUSE |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 SYNOPSIS |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
use Fuse::Simple qw(accessor main); |
11
|
|
|
|
|
|
|
my $var = "this is a variable you can modify. write to me!\n"; |
12
|
|
|
|
|
|
|
my $filesystem = { |
13
|
|
|
|
|
|
|
foo => "this is the contents of a file called foo\n", |
14
|
|
|
|
|
|
|
subdir => { |
15
|
|
|
|
|
|
|
"foo" => "this foo is in a subdir called subdir\n", |
16
|
|
|
|
|
|
|
"blah" => "this blah is in a subdir called subdir\n", |
17
|
|
|
|
|
|
|
}, |
18
|
|
|
|
|
|
|
"blah" => \ "subdir/blah", # scalar refs are symlinks |
19
|
|
|
|
|
|
|
"magic" => sub { return "42\n" }, # will be called to get value |
20
|
|
|
|
|
|
|
"var" => accessor(\$var), # read and write this variable |
21
|
|
|
|
|
|
|
"var2" => accessor(\$var), # and the same variable |
22
|
|
|
|
|
|
|
"var.b" => accessor(\ my $tmp), # and an anonymous var |
23
|
|
|
|
|
|
|
}; |
24
|
|
|
|
|
|
|
main( |
25
|
|
|
|
|
|
|
"mountpoint" => "/mnt", # actually optional |
26
|
|
|
|
|
|
|
"debug" => 0, # for debugging Fuse::Simple. optional |
27
|
|
|
|
|
|
|
"fuse_debug" => 0, # for debugging FUSE itself. optional |
28
|
|
|
|
|
|
|
"threaded" => 0, # optional |
29
|
|
|
|
|
|
|
"/" => $filesystem, # required :-) |
30
|
|
|
|
|
|
|
); |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=head1 DESCRIPTION |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
B lets you write filesystems in Perl. B makes this |
35
|
|
|
|
|
|
|
REALLY Simple, as you just need a hash for your root directory, |
36
|
|
|
|
|
|
|
containing strings for files, more hashes for subdirs, or functions |
37
|
|
|
|
|
|
|
to be called for magical functionality a bit like F. |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=cut |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
###################################################################### |
42
|
|
|
|
|
|
|
# By "Nosey" Nick Waterman of Nilex |
43
|
|
|
|
|
|
|
# http://noseynick.org/ |
44
|
|
|
|
|
|
|
# (C) Copyright 2006 Nilex - All wrongs righted, all rights reserved. |
45
|
|
|
|
|
|
|
###################################################################### |
46
|
|
|
|
|
|
|
# Requirements: |
47
|
2
|
|
|
2
|
|
50600
|
use 5.008; |
|
2
|
|
|
|
|
9
|
|
|
2
|
|
|
|
|
99
|
|
48
|
2
|
|
|
2
|
|
14
|
use strict; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
94
|
|
49
|
2
|
|
|
2
|
|
12
|
use warnings; |
|
2
|
|
|
|
|
9
|
|
|
2
|
|
|
|
|
278
|
|
50
|
2
|
|
|
2
|
|
13
|
use Carp; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
210
|
|
51
|
2
|
|
|
2
|
|
977
|
use Fuse; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
use Errno qw(:POSIX); # ENOENT EISDIR etc |
53
|
|
|
|
|
|
|
use Fcntl qw(:DEFAULT :mode); # S_IFREG S_IFDIR, O_SYNC O_LARGEFILE etc. |
54
|
|
|
|
|
|
|
use Switch; |
55
|
|
|
|
|
|
|
# use diagnostics; |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
###################################################################### |
58
|
|
|
|
|
|
|
# Module stuff: |
59
|
|
|
|
|
|
|
###################################################################### |
60
|
|
|
|
|
|
|
use Exporter; |
61
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
62
|
|
|
|
|
|
|
our $VERSION = '1.00'; |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# thou shalt not pollute, thou shalt not export more than thou needest. |
65
|
|
|
|
|
|
|
our @EXPORT = qw( ); |
66
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
67
|
|
|
|
|
|
|
main fetch runcode saferun fserr nocache wrap quoted |
68
|
|
|
|
|
|
|
dump_open_flags accessor easy_getattr |
69
|
|
|
|
|
|
|
fs_not_imp fs_flush fs_getattr fs_getdir fs_open fs_read fs_readlink |
70
|
|
|
|
|
|
|
fs_release fs_statfs fs_truncate fs_write |
71
|
|
|
|
|
|
|
); |
72
|
|
|
|
|
|
|
our %EXPORT_TAGS = |
73
|
|
|
|
|
|
|
( |
74
|
|
|
|
|
|
|
'all' => \@EXPORT_OK, |
75
|
|
|
|
|
|
|
'DEFAULT' => \@EXPORT, |
76
|
|
|
|
|
|
|
'usual' => [qw(main accessor fserr nocache)], |
77
|
|
|
|
|
|
|
'debug' => [qw(wrap quoted dump_open_flags)], |
78
|
|
|
|
|
|
|
'tools' => [qw(fetch runcode saferun easy_getattr)], |
79
|
|
|
|
|
|
|
'filesys' => [qw( |
80
|
|
|
|
|
|
|
fs_not_imp fs_flush fs_getattr fs_getdir fs_open fs_read |
81
|
|
|
|
|
|
|
fs_readlink fs_release fs_statfs fs_truncate fs_write |
82
|
|
|
|
|
|
|
)], |
83
|
|
|
|
|
|
|
); |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=head1 IMPORT TAGS |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
B exports nothing by default, but individual functions |
88
|
|
|
|
|
|
|
can be exported, or any ofthe following tags: |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
=over |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
=item :usual |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
Includes: main accessor fserr nocache |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=item :debug |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
Includes: wrap quoted dump_open_flags |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=item :tools |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
Includes: fetch runcode saferun easy_getattr |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=item :filesys |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
Includes: |
107
|
|
|
|
|
|
|
fs_not_imp fs_flush fs_getattr fs_getdir fs_open fs_read |
108
|
|
|
|
|
|
|
fs_readlink fs_release fs_statfs fs_truncate fs_write |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=back |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=begin testing |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
BEGIN { use_ok( 'Fuse::Simple', qw(:usual :debug :tools :filesys)); } |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
=end testing |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=cut |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
###################################################################### |
121
|
|
|
|
|
|
|
# Some useful stuff |
122
|
|
|
|
|
|
|
###################################################################### |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
our $debug = 0; # can be set if you really really need it to be |
125
|
|
|
|
|
|
|
my $ctime = time(); |
126
|
|
|
|
|
|
|
my $uid = $>; |
127
|
|
|
|
|
|
|
my $gid = $) + 0; |
128
|
|
|
|
|
|
|
our $fs = { |
129
|
|
|
|
|
|
|
# "empty" dir by default |
130
|
|
|
|
|
|
|
"README" => "You forgot to pass a '/' parameter to Fuse::Simple::main!\n" |
131
|
|
|
|
|
|
|
}; |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
###################################################################### |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=head1 MAIN FUNCTION |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=over |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=item B(B => I, ...) |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
Mount your filesystem, and probably never return. Arguments are: |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=over |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=item B => I<"/mnt">, |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
This is actually optional. If you don't supply a mountpoint, it'll |
148
|
|
|
|
|
|
|
take it from @ARGV ! |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=item B => I<0|1>, |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
Debug Fuse::Simple. All filesystem calls, arguments, and return values |
153
|
|
|
|
|
|
|
will be dumped, a bit like L for perl. |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=item B => I<0|1>, |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
Debug FUSE itself. More low-level than B |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=item B => I<0|1>, |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
See L |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
=item B<"/"> => { hash for your root directory }, |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
=item B B B B B B etc |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
See L |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
You can replace any of the low-level functions if you want, but if |
170
|
|
|
|
|
|
|
you wanted to mess around with the dirty bits, you'd probably not be |
171
|
|
|
|
|
|
|
using L, would you? |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=item others |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
If I've forgotten any L args, you can supply them too. |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=back |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=back |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=cut |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub main { |
184
|
|
|
|
|
|
|
# some default args |
185
|
|
|
|
|
|
|
my %args = ( |
186
|
|
|
|
|
|
|
"mountpoint" => $ARGV[0] || "", |
187
|
|
|
|
|
|
|
"debug" => $debug, |
188
|
|
|
|
|
|
|
"fuse_debug" => 0, |
189
|
|
|
|
|
|
|
"threaded" => 0, |
190
|
|
|
|
|
|
|
"/" => $fs, |
191
|
|
|
|
|
|
|
); |
192
|
|
|
|
|
|
|
# the default subs |
193
|
|
|
|
|
|
|
my %fs_subs = ( |
194
|
|
|
|
|
|
|
"chmod" => \&fs_not_imp, |
195
|
|
|
|
|
|
|
"chown" => \&fs_not_imp, |
196
|
|
|
|
|
|
|
"flush" => \&fs_flush, |
197
|
|
|
|
|
|
|
"fsync" => \&fs_not_imp, |
198
|
|
|
|
|
|
|
"getattr" => \&fs_getattr, |
199
|
|
|
|
|
|
|
"getdir" => \&fs_getdir, |
200
|
|
|
|
|
|
|
"getxattr" => \&fs_not_imp, |
201
|
|
|
|
|
|
|
"link" => \&fs_not_imp, |
202
|
|
|
|
|
|
|
"listxattr" => \&fs_not_imp, |
203
|
|
|
|
|
|
|
"mkdir" => \&fs_not_imp, |
204
|
|
|
|
|
|
|
"mknod" => \&fs_not_imp, |
205
|
|
|
|
|
|
|
"open" => \&fs_open, |
206
|
|
|
|
|
|
|
"read" => \&fs_read, |
207
|
|
|
|
|
|
|
"readlink" => \&fs_readlink, |
208
|
|
|
|
|
|
|
"release" => \&fs_release, |
209
|
|
|
|
|
|
|
"removexattr" => \&fs_not_imp, |
210
|
|
|
|
|
|
|
"rmdir" => \&fs_not_imp, |
211
|
|
|
|
|
|
|
"rename" => \&fs_not_imp, |
212
|
|
|
|
|
|
|
"setxattr" => \&fs_not_imp, |
213
|
|
|
|
|
|
|
"statfs" => \&fs_statfs, |
214
|
|
|
|
|
|
|
"symlink" => \&fs_not_imp, |
215
|
|
|
|
|
|
|
"truncate" => \&fs_truncate, |
216
|
|
|
|
|
|
|
"unlink" => \&fs_not_imp, |
217
|
|
|
|
|
|
|
"utime" => sub{return 0}, |
218
|
|
|
|
|
|
|
"write" => \&fs_write, |
219
|
|
|
|
|
|
|
); |
220
|
|
|
|
|
|
|
my $name; |
221
|
|
|
|
|
|
|
# copy across the arg supplied to main() |
222
|
|
|
|
|
|
|
while ($name = shift) { |
223
|
|
|
|
|
|
|
$args{$name} = shift; |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
# except extract these ones back out. |
226
|
|
|
|
|
|
|
$debug = delete $args{"debug"}; |
227
|
|
|
|
|
|
|
$args{"debug"} = delete( $args{"fuse_debug"} ) || 0; |
228
|
|
|
|
|
|
|
$fs = delete $args{"/"}; |
229
|
|
|
|
|
|
|
# add the functions, if not already defined. |
230
|
|
|
|
|
|
|
# wrap in debugger if debug is set. |
231
|
|
|
|
|
|
|
for $name (keys %fs_subs) { |
232
|
|
|
|
|
|
|
my $sub = $fs_subs{$name}; |
233
|
|
|
|
|
|
|
$sub = wrap($sub, $name) if $debug; |
234
|
|
|
|
|
|
|
$args{$name} ||= $sub; |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
Fuse::main(%args); |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
=head1 UTIL FUNCTIONS |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
These might be useful for people writing their own filesystems |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
=over |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
=item B(I<$path, @args>) (not exported) |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
Given F, return the F dir or file or |
248
|
|
|
|
|
|
|
whatever. @args will be passed to the final coderef if supplied. |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
=begin testing |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
is(fetch("README"), $Fuse::Simple::fs->{README}, "fetch() test"); |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=end testing |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
=cut |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
sub fetch { |
259
|
|
|
|
|
|
|
my ($path, @args) = @_; |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
my $obj = $fs; |
262
|
|
|
|
|
|
|
for my $elem (split '/', $path) { |
263
|
|
|
|
|
|
|
next if $elem eq ""; # skip empty // and before first / |
264
|
|
|
|
|
|
|
$obj = runcode($obj); # if there's anything to run |
265
|
|
|
|
|
|
|
# the dir we're changing into must be a hash (dir) |
266
|
|
|
|
|
|
|
return fserr(ENOTDIR()) unless ref($obj) eq "HASH"; |
267
|
|
|
|
|
|
|
# note that ENOENT and undef are NOT the same thing! |
268
|
|
|
|
|
|
|
return fserr(ENOENT()) unless exists $obj->{$elem}; |
269
|
|
|
|
|
|
|
$obj = $obj->{$elem}; |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
return runcode($obj, @args); |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=item B(I<$code, @args>) (not exported) |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
B, run it, or return our cached version |
278
|
|
|
|
|
|
|
return after all CODE refs have been followed. |
279
|
|
|
|
|
|
|
also returns first arg if it wasn't a coderef. |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
=begin testing |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
is(runcode("foo"), "foo", "runcode with string"); |
284
|
|
|
|
|
|
|
is_deeply(runcode(["A","B","C"]), ["A","B","C"], "runcode with arrayref"); |
285
|
|
|
|
|
|
|
is_deeply(runcode({"A"=>"B"}), {"A"=>"B"}, "runcode with hashref"); |
286
|
|
|
|
|
|
|
is(runcode(undef), undef, "runcode with undef"); |
287
|
|
|
|
|
|
|
is(runcode(sub {return "foo"}), "foo", "runcode with foo"); |
288
|
|
|
|
|
|
|
is(runcode(sub {return shift}, "foo"), "foo", "runcode with an arg"); |
289
|
|
|
|
|
|
|
is_deeply(runcode(sub{return{"a"=>"b"}}, {"a"=>"b"}), {"a"=>"b"}, |
290
|
|
|
|
|
|
|
"runcode sub returns hash"); |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
=end testing |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
=cut |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
my %codecache = (); |
297
|
|
|
|
|
|
|
sub runcode { |
298
|
|
|
|
|
|
|
my ($obj, @args) = @_; |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
while (ref($obj) eq "CODE") { |
301
|
|
|
|
|
|
|
my $old = $obj; |
302
|
|
|
|
|
|
|
if (@args) { # run with these args. don't cache |
303
|
|
|
|
|
|
|
delete $codecache{$old}; |
304
|
|
|
|
|
|
|
print "running $obj(",quoted(@args),") NO CACHE\n" if $debug; |
305
|
|
|
|
|
|
|
$obj = saferun($obj, @args); |
306
|
|
|
|
|
|
|
} elsif (exists $codecache{$obj}) { # found in cache |
307
|
|
|
|
|
|
|
print "got cached $obj\n" if $debug; |
308
|
|
|
|
|
|
|
$obj = $codecache{$obj}; # could be undef, or an error, BTW |
309
|
|
|
|
|
|
|
} else { |
310
|
|
|
|
|
|
|
print "running $obj() to cache\n" if $debug; |
311
|
|
|
|
|
|
|
$obj = $codecache{$old} = saferun($obj); |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
if (ref($obj) eq "NOCACHE") { |
315
|
|
|
|
|
|
|
print "returned a nocache() value - flushing\n" if $debug; |
316
|
|
|
|
|
|
|
delete $codecache{$old}; |
317
|
|
|
|
|
|
|
$obj = $$obj; |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
print "returning ",ref($obj)," ", |
321
|
|
|
|
|
|
|
defined($obj) ? $obj : "undef", |
322
|
|
|
|
|
|
|
"\n" if $debug; |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
return $obj; |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
=item B(I<$sub>,I<@args>) |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
Runs the supplied $sub coderef, safely (IE catches die() etc), |
330
|
|
|
|
|
|
|
returns something usable by the rest of Fuse::Simple. |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
=begin testing |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
is(saferun(sub{"foo"}), "foo", "saferun string"); |
335
|
|
|
|
|
|
|
is(saferun(sub{shift}, "foo"), "foo", "saferun arg"); |
336
|
|
|
|
|
|
|
is(ref(saferun(sub{die "foo"})), "ERROR", "saferun error"); |
337
|
|
|
|
|
|
|
is_deeply(saferun(sub{die ["foo"]}), ["foo"], "saferun array die"); |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
=end testing |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
=cut |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
sub saferun { |
344
|
|
|
|
|
|
|
my ($sub, @args) = @_; |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
my $ret = eval { &$sub(@args) }; |
347
|
|
|
|
|
|
|
my $died = $@; |
348
|
|
|
|
|
|
|
if (ref($died)) { |
349
|
|
|
|
|
|
|
# we can die fserr(ENOTSUP) if we want! |
350
|
|
|
|
|
|
|
print "+++ Error $$died\n" if ref($died) eq "ERROR"; |
351
|
|
|
|
|
|
|
return $died; |
352
|
|
|
|
|
|
|
} elsif ($died) { |
353
|
|
|
|
|
|
|
print "+++ $died\n"; |
354
|
|
|
|
|
|
|
# stale file handle? moreorless? |
355
|
|
|
|
|
|
|
return fserr(ESTALE()); |
356
|
|
|
|
|
|
|
} |
357
|
|
|
|
|
|
|
return $ret; |
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
=item B(I<$error_number>) |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
Used by called coderef files, to return an error indication, for example: |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
return fserr(E2BIG()); |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
=begin testing |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
is(ref(fserr("foo")), "ERROR", "fserr ref type"); |
369
|
|
|
|
|
|
|
is(${&fserr("foo")}, "foo", "fserr arg passed"); |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
=end testing |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
=cut |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
sub fserr { |
376
|
|
|
|
|
|
|
return bless(\ shift, "ERROR"); # yup, utter abuse of bless :-) |
377
|
|
|
|
|
|
|
} |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
=item B(I<$stuff_to_return>) |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
Used by called coderef files, to return something that should not be cached. |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
=begin testing |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
is(ref(nocache("foo")), "NOCACHE", "nocache ref type"); |
386
|
|
|
|
|
|
|
is(${&nocache("foo")}, "foo", "nocache arg passed"); |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
=end testing |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
=cut |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
sub nocache { |
393
|
|
|
|
|
|
|
return bless(\ shift, "NOCACHE"); # yup, utter abuse of bless :-) |
394
|
|
|
|
|
|
|
} |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
=item B(I<$sub, @name_etc>) |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
Wrap a function with something that'll dump args on the way in |
399
|
|
|
|
|
|
|
and return values on the way out. |
400
|
|
|
|
|
|
|
This is a debugging fuction, sorta like L for perl really. |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
=begin testing |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
my $test = wrap(sub {return "foo".(shift||"")}, "foo"); |
405
|
|
|
|
|
|
|
is(ref($test), "CODE", "wrap a coderef"); |
406
|
|
|
|
|
|
|
is(&$test(), "foo", "wrapped coderef returns expected"); |
407
|
|
|
|
|
|
|
is(&$test("bar"), "foobar", "wrapped coderef args work"); |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
=end testing |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
=cut |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
my @indent = (); |
414
|
|
|
|
|
|
|
sub wrap { |
415
|
|
|
|
|
|
|
my ($sub, @name_etc) = @_; |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
return sub { |
418
|
|
|
|
|
|
|
print "@indent> @name_etc(", quoted(@_), ")\n"; |
419
|
|
|
|
|
|
|
push @indent, " "; |
420
|
|
|
|
|
|
|
my @ret = eval { &$sub(@_) }; |
421
|
|
|
|
|
|
|
my $died = $@; |
422
|
|
|
|
|
|
|
pop @indent; |
423
|
|
|
|
|
|
|
die $died if ref($died); # die(some object), EG die(fserr(E2BIG)) |
424
|
|
|
|
|
|
|
die "@indent! $died" if $died; |
425
|
|
|
|
|
|
|
print "@indent< =", quoted(@ret), "\n"; |
426
|
|
|
|
|
|
|
return wantarray ? @ret : $ret[0]; |
427
|
|
|
|
|
|
|
}; |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
=item B(I<@list>) |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
return a nice printable version of the args, a little like |
433
|
|
|
|
|
|
|
Data::Dumper would |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
=begin testing |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
is(quoted("foo"), '"foo"', "quoting"); |
438
|
|
|
|
|
|
|
is(quoted('\\'), '"\\\\"', "quoting backslash"); |
439
|
|
|
|
|
|
|
is(quoted("\$\@\"\t\r\n\f\a\e"), '"\$\@\"\t\r\n\f\a\e"', "quoting fun"); |
440
|
|
|
|
|
|
|
is(quoted('42'), '42', "unquoted numbers"); |
441
|
|
|
|
|
|
|
is(quoted(1,2,3), '1, 2, 3', "quoted list"); |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
=end testing |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
=cut |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
my %escaped = ( |
448
|
|
|
|
|
|
|
'$' => '$', '@' => '@', '"' => '"', "\\" => "\\", |
449
|
|
|
|
|
|
|
"\t" => "t", "\r" => "r", "\n" => "n", |
450
|
|
|
|
|
|
|
"\f" => "f", "\a" => "a", "\e" => "e", |
451
|
|
|
|
|
|
|
); |
452
|
|
|
|
|
|
|
sub quoted { |
453
|
|
|
|
|
|
|
my @ret = (); |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
for my $n (@_) { |
456
|
|
|
|
|
|
|
# special case for undefined vars: |
457
|
|
|
|
|
|
|
if (not defined($n)) { push @ret, "undef"; next; } |
458
|
|
|
|
|
|
|
# digits (that are really digits without newlines) can be printed |
459
|
|
|
|
|
|
|
# without quoting: |
460
|
|
|
|
|
|
|
if ($n =~ /^-?\d+\.?\d*$/ && $n !~ /\n/) { push @ret, $n; next; } |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
# other stuff needs quoting and escaping in fun ways: |
463
|
|
|
|
|
|
|
my $s = $n; |
464
|
|
|
|
|
|
|
$s =~ s/([\$\@\"\\\t\n\r\f\a\e])/\\$escaped{$1}/g; |
465
|
|
|
|
|
|
|
$s =~ s/([^ -~])/sprintf('\x{%x}',ord($1))/ge; |
466
|
|
|
|
|
|
|
push @ret, '"'.$s.'"'; |
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
return join(", ", @ret); |
469
|
|
|
|
|
|
|
} |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
=item B(I<$flags>) |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
Translate the flags to the open() call |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
=cut |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
sub dump_open_flags { |
478
|
|
|
|
|
|
|
my $flags = shift; |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
printf " flags: 0%o = (", $flags; |
481
|
|
|
|
|
|
|
for my $bits ( |
482
|
|
|
|
|
|
|
[ O_ACCMODE(), O_RDONLY(), "O_RDONLY" ], |
483
|
|
|
|
|
|
|
[ O_ACCMODE(), O_WRONLY(), "O_WRONLY" ], |
484
|
|
|
|
|
|
|
[ O_ACCMODE(), O_RDWR(), "O_RDWR" ], |
485
|
|
|
|
|
|
|
[ O_APPEND(), O_APPEND(), "|O_APPEND" ], |
486
|
|
|
|
|
|
|
[ O_NONBLOCK(), O_NONBLOCK(), "|O_NONBLOCK" ], |
487
|
|
|
|
|
|
|
[ O_SYNC(), O_SYNC(), "|O_SYNC" ], |
488
|
|
|
|
|
|
|
[ O_DIRECT(), O_DIRECT(), "|O_DIRECT" ], |
489
|
|
|
|
|
|
|
[ O_LARGEFILE(), O_LARGEFILE(), "|O_LARGEFILE" ], |
490
|
|
|
|
|
|
|
[ O_NOFOLLOW(), O_NOFOLLOW(), "|O_NOFOLLOW" ], |
491
|
|
|
|
|
|
|
) { |
492
|
|
|
|
|
|
|
my ($mask, $flag, $name) = @$bits; |
493
|
|
|
|
|
|
|
if (($flags & $mask) == $flag) { |
494
|
|
|
|
|
|
|
$flags -= $flag; |
495
|
|
|
|
|
|
|
print $name; |
496
|
|
|
|
|
|
|
} |
497
|
|
|
|
|
|
|
} |
498
|
|
|
|
|
|
|
printf "| 0%o !!!", $flags if $flags; |
499
|
|
|
|
|
|
|
print ")\n"; |
500
|
|
|
|
|
|
|
} |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
=item B(I<\$var>) |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
return a sub that can be used to read and write the (scalar) variable $var: |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
my $var = "default value"; |
507
|
|
|
|
|
|
|
my $fs = { "filename" => accessor(\$var) }; |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
This accessor is a bit over-simple, doesn't handle multi-block writes, |
510
|
|
|
|
|
|
|
partial block writes, seeked reads, non-saclar values, |
511
|
|
|
|
|
|
|
or anything particularly clever. |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
=begin testing |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
my $foo = undef; |
516
|
|
|
|
|
|
|
my $acc = accessor(\$foo); |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
is(ref($acc), "CODE", "accessor is a coderef"); |
519
|
|
|
|
|
|
|
is($foo, undef, "undef at first"); |
520
|
|
|
|
|
|
|
is(&$acc(), undef, "undef thru accessor"); |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
&$acc("foo"); |
523
|
|
|
|
|
|
|
is($foo, "foo", "foo was set"); |
524
|
|
|
|
|
|
|
is(&$acc(), "foo", "foo thru accessor"); |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
$foo="bar"; |
527
|
|
|
|
|
|
|
is(&$acc(), "bar", "bar thru accessor"); |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
=end testing |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
=cut |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
sub accessor { |
534
|
|
|
|
|
|
|
my $var_ref = shift; |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
croak "accessor() requires a reference to a scalar var\n" |
537
|
|
|
|
|
|
|
unless defined($var_ref) && ref($var_ref) eq "SCALAR"; |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
return sub { |
540
|
|
|
|
|
|
|
my $new = shift; |
541
|
|
|
|
|
|
|
$$var_ref = $new if defined($new); |
542
|
|
|
|
|
|
|
return $$var_ref; |
543
|
|
|
|
|
|
|
} |
544
|
|
|
|
|
|
|
} |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
=item B(I<$mode, $size>) |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
Internal function, to make it easier to return Bs 13 |
549
|
|
|
|
|
|
|
arguments when there's probably only 2 you really care about. |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
Returns everything else that getattr() should. |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
=back |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
=cut |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
sub easy_getattr { |
558
|
|
|
|
|
|
|
my ($mode, $size) = @_; |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
return ( |
561
|
|
|
|
|
|
|
0, 0, # $dev, $ino, |
562
|
|
|
|
|
|
|
$mode, |
563
|
|
|
|
|
|
|
1, # $nlink, see fuse.sourceforge.net/wiki/index.php/FAQ |
564
|
|
|
|
|
|
|
$uid, $gid, # $uid, $gid, |
565
|
|
|
|
|
|
|
0, # $rdev, |
566
|
|
|
|
|
|
|
$size, # $size, |
567
|
|
|
|
|
|
|
$ctime, $ctime, $ctime, # actually $atime, $mtime, $ctime, |
568
|
|
|
|
|
|
|
1024, 1, # $blksize, $blocks, |
569
|
|
|
|
|
|
|
); |
570
|
|
|
|
|
|
|
} |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
=head1 FUSE FILESYSTEM FUNCTIONS |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
These can be overridden if you really want to get at the guts of the |
575
|
|
|
|
|
|
|
filesystem, but if you really wanted to get that dirty, you probably |
576
|
|
|
|
|
|
|
wouldn't be using Fuse::Simple, would you? |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
=over |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
=item B() |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
return ENOSYS "Function not implemented" to the program that's |
583
|
|
|
|
|
|
|
accessing this function. |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
=begin testing |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
is(fs_not_imp(), -38, "fs_not_imp -38"); |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
=end testing |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
=cut |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
sub fs_not_imp { return -ENOSYS() } |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
=item B(I<$path>) |
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
=begin testing |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
is(fs_flush(), 0, "fs_flush"); |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
=end testing |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
=cut |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
sub fs_flush { |
606
|
|
|
|
|
|
|
# we're passed a path, but finding my coderef stuff from a path |
607
|
|
|
|
|
|
|
# is a bit of a 'mare. flush the lot, won't hurt TOO much. |
608
|
|
|
|
|
|
|
print "Flushing\n" if $debug; |
609
|
|
|
|
|
|
|
%codecache = (); |
610
|
|
|
|
|
|
|
return 0; |
611
|
|
|
|
|
|
|
} |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
=item B(I<$path>) |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
=cut |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
sub fs_getattr { |
618
|
|
|
|
|
|
|
my $path = shift; |
619
|
|
|
|
|
|
|
my $obj = fetch($path); |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
# undef doesn't actually mean "file not found", it could be a coderef |
622
|
|
|
|
|
|
|
# file-sub which has returned undef. |
623
|
|
|
|
|
|
|
return easy_getattr(S_IFREG | 0200, 0) unless defined($obj); |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
switch (ref($obj)) { |
626
|
|
|
|
|
|
|
case "ERROR" { # this is an error to be returned. |
627
|
|
|
|
|
|
|
return -$$obj; |
628
|
|
|
|
|
|
|
} |
629
|
|
|
|
|
|
|
case "" { # this isn't a ref, it's a real string "file" |
630
|
|
|
|
|
|
|
return easy_getattr(S_IFREG | 0644, length($obj)); |
631
|
|
|
|
|
|
|
} |
632
|
|
|
|
|
|
|
# case "CODE" should never happen - already been run by fetch() |
633
|
|
|
|
|
|
|
case "HASH" { # this is a directory hash |
634
|
|
|
|
|
|
|
return easy_getattr(S_IFDIR | 0755, 1); |
635
|
|
|
|
|
|
|
} |
636
|
|
|
|
|
|
|
case "SCALAR" { # this is a scalar ref. we use these for symlinks. |
637
|
|
|
|
|
|
|
return easy_getattr(S_IFLNK | 0777, 1); |
638
|
|
|
|
|
|
|
} |
639
|
|
|
|
|
|
|
else { # what the hell is this file?!? |
640
|
|
|
|
|
|
|
print "+++ What on earth is ",ref($obj)," $path ?\n"; |
641
|
|
|
|
|
|
|
return easy_getattr(S_IFREG | 0000, 0); |
642
|
|
|
|
|
|
|
} |
643
|
|
|
|
|
|
|
} |
644
|
|
|
|
|
|
|
} |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
=item B(I<$path>) |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
=cut |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
sub fs_getdir { |
651
|
|
|
|
|
|
|
my $obj = fetch(shift); |
652
|
|
|
|
|
|
|
return -$$obj if ref($obj) eq "ERROR"; # THINK this is a good idea. |
653
|
|
|
|
|
|
|
return -ENOENT() unless ref($obj) eq "HASH"; |
654
|
|
|
|
|
|
|
return (".", "..", sort(keys %$obj), 0); |
655
|
|
|
|
|
|
|
} |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
=item B(I<$path, $flags>) |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
=cut |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
sub fs_open { |
662
|
|
|
|
|
|
|
# doesn't really need to open, just needs to check. |
663
|
|
|
|
|
|
|
my $obj = fetch(shift); |
664
|
|
|
|
|
|
|
my $flags = shift; |
665
|
|
|
|
|
|
|
dump_open_flags($flags) if $debug; |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
# if it's undefined, and we're not writing to it, return an error |
668
|
|
|
|
|
|
|
return -EBADF() unless defined($obj) or ($flags & O_ACCMODE()); |
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
switch (ref($obj)) { |
671
|
|
|
|
|
|
|
case "ERROR" { return -$$obj; } |
672
|
|
|
|
|
|
|
case "" { return 0 } # this is a real string "file" |
673
|
|
|
|
|
|
|
case "HASH" { return -EISDIR(); } # this is a directory hash |
674
|
|
|
|
|
|
|
else { return -ENOSYS(); } # what the hell is this file?!? |
675
|
|
|
|
|
|
|
} |
676
|
|
|
|
|
|
|
} |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
=item B(I<$path, $size, $offset>) |
679
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
=cut |
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
sub fs_read { |
683
|
|
|
|
|
|
|
my $obj = fetch(shift); |
684
|
|
|
|
|
|
|
my $size = shift; |
685
|
|
|
|
|
|
|
my $off = shift; |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
return -ENOENT() unless defined($obj); |
688
|
|
|
|
|
|
|
return -$$obj if ref($obj) eq "ERROR"; |
689
|
|
|
|
|
|
|
# any other types of refs are probably bad |
690
|
|
|
|
|
|
|
return -ENOENT() if ref($obj); |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
if ($off > length($obj)) { |
693
|
|
|
|
|
|
|
return -EINVAL(); |
694
|
|
|
|
|
|
|
} elsif ($off == length($obj)) { |
695
|
|
|
|
|
|
|
return 0; # EOF |
696
|
|
|
|
|
|
|
} |
697
|
|
|
|
|
|
|
return substr($obj, $off, $size); |
698
|
|
|
|
|
|
|
} |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
=item B(I<$path>) |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
=cut |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
sub fs_readlink { |
705
|
|
|
|
|
|
|
my $obj = fetch(shift); |
706
|
|
|
|
|
|
|
return -$$obj if ref($obj) eq "ERROR"; |
707
|
|
|
|
|
|
|
return -EINVAL() unless ref($obj) eq "SCALAR"; |
708
|
|
|
|
|
|
|
return $$obj; |
709
|
|
|
|
|
|
|
} |
710
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
=item B(I<$path, $flags>) |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
=cut |
714
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
sub fs_release { |
716
|
|
|
|
|
|
|
my ($path, $flags) = @_; |
717
|
|
|
|
|
|
|
dump_open_flags($flags) if $debug; |
718
|
|
|
|
|
|
|
return 0; |
719
|
|
|
|
|
|
|
} |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
=item B() |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
=cut |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
sub fs_statfs { |
726
|
|
|
|
|
|
|
return ( |
727
|
|
|
|
|
|
|
255, # $namelen, |
728
|
|
|
|
|
|
|
1,1, # $files, $files_free, |
729
|
|
|
|
|
|
|
1,1, # $blocks, $blocks_avail, # 0,0 seems to hide it from df? |
730
|
|
|
|
|
|
|
2, # $blocksize, |
731
|
|
|
|
|
|
|
); |
732
|
|
|
|
|
|
|
} |
733
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
=item B(I<$path, $offset>) |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
=cut |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
sub fs_truncate { |
739
|
|
|
|
|
|
|
my $obj = fetch(shift, ""); # run anything to set it to "" |
740
|
|
|
|
|
|
|
return -$$obj if ref($obj) eq "ERROR"; |
741
|
|
|
|
|
|
|
return 0; |
742
|
|
|
|
|
|
|
} |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
=item B(I<$path, $buffer, $offset>) |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
=cut |
747
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
sub fs_write { |
749
|
|
|
|
|
|
|
my ($path, $buf, $off) = @_; |
750
|
|
|
|
|
|
|
my $obj = fetch($path, $buf, $off); # this runs the coderefs! |
751
|
|
|
|
|
|
|
return -$$obj if ref($obj) eq "ERROR"; |
752
|
|
|
|
|
|
|
return length($buf); |
753
|
|
|
|
|
|
|
} |
754
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
1; # for use() or require() |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
__END__ |