Documentation cleanups.
[perl-fuse.git] / examples / filter_attr_fs.pl
1 #!/usr/bin/perl -w
2 # filter_attr_t.pl
3 # Loopback fs that shows only files with a particular xattr
4
5 # (c) Reuben Thomas  29/11/2007-5/1/2008, based on example code from Fuse package
6
7 use strict;
8 #use blib;
9
10 use Fuse;
11 use File::ExtAttr ':all';
12 use IO::File;
13 use POSIX qw(ENOENT ENOSYS EEXIST EPERM O_RDONLY O_RDWR O_APPEND O_CREAT O_ACCMODE);
14 use Fcntl qw(S_ISBLK S_ISCHR S_ISFIFO SEEK_SET);
15
16 # Debug flag
17 my $debug = 0;
18
19 # Global settings
20 my ($tag, $real_root, $mountpoint);
21
22
23 sub debug {
24         print STDERR shift if $debug ne 0;
25 }
26
27 my $can_syscall = eval {
28         require 'syscall.ph'; # for SYS_mknod and SYS_lchown
29 };
30
31 if (!$can_syscall && open my $fh, '<', '/usr/include/sys/syscall.h') {
32         my %sys = do { local $/ = undef;
33                         <$fh> =~ m/\#define \s+ (\w+) \s+ (\d+)/gxms;
34                 };
35         close $fh;
36         if ($sys{SYS_mknod} && $sys{SYS_lchown}) {
37                 *SYS_mknod  = sub { $sys{SYS_mknod}  };
38                 *SYS_lchown = sub { $sys{SYS_lchown} };
39                 $can_syscall = 1;
40         }
41 }
42
43 sub tagged {
44         my ($file) = @_;
45         $file =~ s|/$||;
46         my $ret = getfattr($file, $tag);
47         debug("tagged: $file $tag " . defined($ret) . "\n");
48         return $ret;
49 }
50
51 sub tag {
52         return setfattr(shift, $tag, "");
53 }
54
55 sub detag {
56         return delfattr(shift, $tag);
57 }
58
59 sub append_root {
60         return $real_root . shift;
61 }
62
63 sub err {
64         my ($err) = @_;
65         return $err ? 0 : -$!;
66 }
67
68 sub x_getattr {
69         debug("x_getattr ");
70         my ($file) = append_root(shift);
71         return -ENOENT() unless tagged($file);
72         my (@list) = lstat($file);
73         return -$! unless @list;
74         return @list;
75 }
76
77 sub x_readlink {
78         debug("x_readlink ");
79         return readlink(append_root(shift));
80 }
81
82 sub x_getdir {
83         debug("x_getdir ");
84         my ($dirname) = append_root(shift);
85         return -ENOENT() unless tagged($dirname) && opendir(DIRHANDLE, $dirname);
86         my (@files) = readdir(DIRHANDLE);
87         closedir(DIRHANDLE);
88         my @psifiles = grep {tagged("$dirname/$_")} @files;
89         return (@psifiles, 0);
90 }
91
92 sub x_mknod {
93         my ($file, $modes, $dev) = @_;
94         return -ENOSYS() if !$can_syscall;
95         debug("x_mknod ");
96         $file = append_root($file);
97         return -EEXIST() if -e $file && !tagged($file);
98         $! = 0;
99         syscall(&SYS_mknod, $file, $modes, $dev);
100         return -$! if $! != 0;
101         return err(tag($file));
102 }
103
104 sub x_mkdir {
105         debug("x_mkdir ");
106         my ($name, $perm) = @_;
107         $name = append_root($name);
108         debug("$name");
109         my $ret = err(mkdir $name, $perm);
110         return $ret if $ret != 0;
111         return err(tag($name));
112 }
113
114 sub x_open {
115         my ($file) = append_root(shift);
116         my ($mode) = shift;
117         my $accmode = $mode & O_ACCMODE;
118         debug("x_open $accmode " . O_ACCMODE . " " . O_WRONLY . " " . O_RDWR . " ");
119         if ($accmode == O_WRONLY || $accmode == O_RDWR) {
120                 return -EEXIST() if -e $file && !tagged($file);
121         } else {
122                 return -ENOENT() unless tagged($file);
123         }
124         return -$! unless sysopen(FILE, $file, $mode);
125         close(FILE);
126         return 0;
127 }
128
129 sub x_read {
130         debug("x_read ");
131         my ($file, $bufsize, $off) = @_;
132         my ($rv) = -ENOSYS();
133         my ($handle) = new IO::File;
134         $file = append_root($file);
135         return -ENOENT() unless tagged($file);
136         my ($fsize) = -s $file;
137         return -ENOSYS() unless open($handle, $file);
138         if(seek($handle, $off, SEEK_SET)) {
139                 read($handle, $rv, $bufsize);
140         }
141         return $rv;
142 }
143
144 sub x_write {
145         debug("x_write ");
146         my ($file, $buf, $off) = @_;
147         my ($rv);
148         $file = append_root($file);
149         return -ENOENT() unless tagged($file);
150         my ($fsize) = -s $file;
151         return -ENOSYS() unless open(FILE, '+<', $file);
152         if ($rv = seek(FILE, $off, SEEK_SET)) {
153                 $rv = print(FILE $buf);
154         }
155         $rv = -ENOSYS() unless $rv;
156         close(FILE);
157         return length($buf);
158 }
159
160 sub x_unlink {
161         debug("x_unlink ");
162         my ($file) = append_root(shift);
163         return -ENOENT() unless tagged($file);
164         return err(detag($file));
165 }
166
167 sub x_symlink {
168         debug("x_symlink ");
169         my ($old) = shift;
170         my ($new) = append_root(shift);
171         return -EEXIST() if -e $new && !tagged($new);
172         return err(symlink($old, $new));
173 }
174
175 sub x_rename {
176         debug("x_rename ");
177         my ($old) = append_root(shift);
178         my ($new) = append_root(shift);
179         return -ENOENT() unless tagged($old);
180         return -EEXIST() unless !-e $new || tagged($new);
181         my ($err) = rename($old, $new) ? 0 : -ENOENT();
182         return $err;
183 }
184
185 sub x_link {
186         debug("x_link ");
187         my ($old) = append_root(shift);
188         my ($new) = append_root(shift);
189         return -ENOENT() unless tagged($old);
190         return -EEXIST() unless !-e $new || tagged($new);
191         return err(link($old, $new));
192 }
193
194 sub x_chown {
195         return -ENOSYS() if !$can_syscall;
196         debug("x_chown ");
197         my ($fn) = append_root(shift);
198         return -ENOENT() unless tagged($fn);
199         my ($uid, $gid) = @_;
200         # perl's chown() does not chown symlinks, it chowns the symlink's
201         # target. It fails when the link's target doesn't exist, because
202         # the stat64() syscall fails.
203         # This causes error messages when unpacking symlinks in tarballs.
204         my ($err) = syscall(&SYS_lchown, $fn, $uid, $gid, $fn) ? -$! : 0;
205         return $err;
206 }
207
208 sub x_chmod {
209         debug("x_chmod ");
210         my ($fn) = append_root(shift);
211         return -ENOENT() unless tagged($fn);
212         my ($mode) = shift;
213         return err(chmod($mode, $fn));
214 }
215
216 sub x_truncate {
217         debug("x_truncate ");
218         my ($fn) = append_root(shift);
219         return -ENOENT() unless tagged($fn);
220         return err(truncate($fn, shift));
221 }
222
223 sub x_utime {
224         debug("x_utime ");
225         my ($fn) = append_root($_[0]);
226         return -ENOENT() unless tagged($fn);
227         return err(utime($_[1], $_[2], $fn));
228 }
229
230 sub x_rmdir {
231         debug("x_rmdir ");
232         my $dir = append_root(shift);
233         return -ENOENT() unless tagged($dir);
234         return err(detag($dir));
235 }
236
237 sub x_statfs {
238         debug("x_statfs\n");
239         my $name = append_root(shift);
240         my($bsize, $frsize, $blocks, $bfree, $bavail,
241                 $files, $ffree, $favail, $fsid, $basetype, $flag,
242                 $namemax, $fstr) = statvfs($real_root) || return -$!;
243         return ($namemax, $files, $ffree, $blocks, $bavail, $bsize);
244 }
245
246 # If you run the script directly, it will run fusermount, which will in turn
247 # re-run this script.  Hence the funky semantics.
248
249 # Parse command-line arguments
250 $mountpoint = "";
251 if (@ARGV) {
252         $tag = shift(@ARGV);
253         $real_root = shift(@ARGV);
254         $mountpoint = shift(@ARGV);
255 }
256
257 # Start up FUSE
258 Fuse::main(
259         mountpoint=>$mountpoint,
260 #       debug   => 1,
261         getattr =>"main::x_getattr",
262         readlink=>"main::x_readlink",
263         getdir  =>"main::x_getdir",
264         mknod   =>"main::x_mknod",
265         mkdir   =>"main::x_mkdir",
266         unlink  =>"main::x_unlink",
267         rmdir   =>"main::x_rmdir",
268         symlink =>"main::x_symlink",
269         rename  =>"main::x_rename",
270         link    =>"main::x_link",
271         chmod   =>"main::x_chmod",
272         chown   =>"main::x_chown",
273         truncate=>"main::x_truncate",
274         utime   =>"main::x_utime",
275         open    =>"main::x_open",
276         read    =>"main::x_read",
277         write   =>"main::x_write",
278         statfs  =>"main::x_statfs",
279 );