fb310141c5837d3c415ded5e9476a345de81571e
[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 # Reuben Thomas  29th November 2007, 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 = 1;
18
19 # Global settings
20 my ($tag, $real_root, $mountpoint);
21
22
23 sub debug {
24         print STDERR shift if $debug;
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         tag($file) if $! == 0;
101         return -$!;
102 }
103
104 sub x_mkdir {
105         debug("x_mkdir ");
106         my ($name, $perm) = @_;
107         $name = append_root($name);
108         return err(mkdir($name, $perm));
109 }
110
111 sub x_open {
112         my ($file) = append_root(shift);
113         my ($mode) = shift;
114         my $accmode = $mode & O_ACCMODE;
115         debug("x_open $accmode " . O_ACCMODE . " " . O_WRONLY . " " . O_RDWR . " ");
116         if ($accmode == O_WRONLY || $accmode == O_RDWR) {
117                 return -EEXIST() if -e $file && !tagged($file);
118         } else {
119                 return -ENOENT() unless tagged($file);
120         }
121         return -$! unless sysopen(FILE, $file, $mode);
122         close(FILE);
123         return 0;
124 }
125
126 sub x_read {
127         debug("x_read ");
128         my ($file, $bufsize, $off) = @_;
129         my ($rv) = -ENOSYS();
130         my ($handle) = new IO::File;
131         $file = append_root($file);
132         return -ENOENT() unless tagged($file);
133         my ($fsize) = -s $file;
134         return -ENOSYS() unless open($handle, $file);
135         if(seek($handle, $off, SEEK_SET)) {
136                 read($handle, $rv, $bufsize);
137         }
138         return $rv;
139 }
140
141 sub x_write {
142         debug("x_write ");
143         my ($file, $buf, $off) = @_;
144         my ($rv);
145         $file = append_root($file);
146         return -ENOENT() unless tagged($file);
147         my ($fsize) = -s $file;
148         return -ENOSYS() unless open(FILE, '+<', $file);
149         if ($rv = seek(FILE, $off, SEEK_SET)) {
150                 $rv = print(FILE $buf);
151         }
152         $rv = -ENOSYS() unless $rv;
153         close(FILE);
154         return length($buf);
155 }
156
157 sub x_unlink {
158         debug("x_unlink ");
159         my ($file) = append_root(shift);
160         return -ENOENT() unless tagged($file);
161         return err(detag($file));
162 }
163
164 sub x_symlink {
165         debug("x_symlink ");
166         my ($old) = shift;
167         my ($new) = append_root(shift);
168         return -EEXIST() if -e $new && !tagged($new);
169         return err(symlink($old, $new));
170 }
171
172 sub x_rename {
173         debug("x_rename ");
174         my ($old) = append_root(shift);
175         my ($new) = append_root(shift);
176         return -ENOENT() unless tagged($old);
177         return -EEXIST() unless !-e $new || tagged($new);
178         my ($err) = rename($old, $new) ? 0 : -ENOENT();
179         return $err;
180 }
181
182 sub x_link {
183         debug("x_link ");
184         my ($old) = append_root(shift);
185         my ($new) = append_root(shift);
186         return -ENOENT() unless tagged($old);
187         return -EEXIST() unless !-e $new || tagged($new);
188         return err(link($old, $new));
189 }
190
191 sub x_chown {
192         return -ENOSYS() if !$can_syscall;
193         debug("x_chown ");
194         my ($fn) = append_root(shift);
195         return -ENOENT() unless tagged($fn);
196         my ($uid, $gid) = @_;
197         # perl's chown() does not chown symlinks, it chowns the symlink's
198         # target.  it fails when the link's target doesn't exist, because
199         # the stat64() syscall fails.
200         # this causes error messages when unpacking symlinks in tarballs.
201         my ($err) = syscall(&SYS_lchown, $fn, $uid, $gid, $fn) ? -$! : 0;
202         return $err;
203 }
204
205 sub x_chmod {
206         debug("x_chmod ");
207         my ($fn) = append_root(shift);
208         return -ENOENT() unless tagged($fn);
209         my ($mode) = shift;
210         return err(chmod($mode, $fn));
211 }
212
213 sub x_truncate {
214         debug("x_truncate ");
215         my ($fn) = append_root(shift);
216         return -ENOENT() unless tagged($fn);
217         return err(truncate($fn, shift));
218 }
219
220 sub x_utime {
221         debug("x_utime ");
222         my ($fn) = append_root($_[0]);
223         return -ENOENT() unless tagged($fn);
224         return err(utime($_[1], $_[2], $fn));
225 }
226
227 sub x_rmdir {
228         debug("x_rmdir ");
229         my $dir = append_root(shift);
230         return -ENOENT() unless tagged($dir);
231         return err(detag($dir));
232 }
233
234 sub x_statfs {
235         debug("x_statfs\n");
236         my $name = append_root(shift);
237         my($bsize, $frsize, $blocks, $bfree, $bavail,
238                 $files, $ffree, $favail, $fsid, $basetype, $flag,
239                 $namemax, $fstr) = statvfs($real_root) || return -$!;
240         return ($namemax, $files, $ffree, $blocks, $bavail, $bsize);
241 }
242
243 # If you run the script directly, it will run fusermount, which will in turn
244 # re-run this script.  Hence the funky semantics.
245
246 # Parse command-line arguments
247 $mountpoint = "";
248 if (@ARGV) {
249         $tag = shift(@ARGV);
250         $real_root = shift(@ARGV);
251         $mountpoint = shift(@ARGV);
252 }
253
254 # Start up FUSE
255 Fuse::main(
256         mountpoint=>$mountpoint,
257 #       debug   => 1,
258         getattr =>"main::x_getattr",
259         readlink=>"main::x_readlink",
260         getdir  =>"main::x_getdir",
261         mknod   =>"main::x_mknod",
262         mkdir   =>"main::x_mkdir",
263         unlink  =>"main::x_unlink",
264         rmdir   =>"main::x_rmdir",
265         symlink =>"main::x_symlink",
266         rename  =>"main::x_rename",
267         link    =>"main::x_link",
268         chmod   =>"main::x_chmod",
269         chown   =>"main::x_chown",
270         truncate=>"main::x_truncate",
271         utime   =>"main::x_utime",
272         open    =>"main::x_open",
273         read    =>"main::x_read",
274         write   =>"main::x_write",
275         statfs  =>"main::x_statfs",
276 );