Added examples for ioctl functionality. Currently linux dependent.
[perl-fuse.git] / examples / fioc.pl
1 #!/usr/bin/env perl
2
3 use strict;
4 no strict qw(refs);
5
6 use Carp ();
7 local $SIG{'__WARN__'} = \&Carp::cluck;
8
9 use Fuse;
10 use Fcntl qw(:mode);
11 use Errno qw(:POSIX);
12 use POSIX;
13
14 my $fioc_size = 0;
15 use constant FIOC_NAME => 'fioc';
16 my $fioc_buf = '';
17 use constant FIOC_NONE  => 0;
18 use constant FIOC_ROOT  => 1;
19 use constant FIOC_FILE  => 2;
20
21 sub _IOC_NRBITS  { 8 }
22 sub _IOC_NRMASK  { ( 1 << &_IOC_NRBITS ) - 1 }
23 sub _IOC_NRSHIFT { 0 }
24
25 sub _IOC_TYPEBITS  { 8 }
26 sub _IOC_TYPEMASK  { ( 1 << &_IOC_TYPEBITS ) - 1 }
27 sub _IOC_TYPESHIFT { &_IOC_NRSHIFT + &_IOC_NRBITS }
28
29 sub _IOC_SIZEBITS { ( POSIX::uname() )[4] =~ /^i[3-6]86|x86_64$/ ? 14 : 13 }
30 sub _IOC_SIZEMASK  { ( 1 << &_IOC_SIZEBITS ) - 1 }
31 sub _IOC_SIZESHIFT { &_IOC_TYPESHIFT + &_IOC_TYPEBITS }
32
33 sub _IOC_DIRBITS  { 32 - &_IOC_NRBITS - &_IOC_TYPEBITS - &_IOC_SIZEBITS }
34 sub _IOC_DIRMASK  { ( 1 << &_IOC_DIRBITS ) - 1 }
35 sub _IOC_DIRSHIFT { &_IOC_SIZESHIFT + &_IOC_SIZEBITS }
36
37 sub _IOC_NONE  { ( POSIX::uname() )[4] =~ /^i[3-6]86|x86_64$/ ? 0 : 1 }
38 sub _IOC_WRITE { ( POSIX::uname() )[4] =~ /^i[3-6]86|x86_64$/ ? 1 : 4 }
39 sub _IOC_READ  { ( POSIX::uname() )[4] =~ /^i[3-6]86|x86_64$/ ? 2 : 2 }
40
41 sub _IOC ($$$$) {
42   ( $_[0] << &_IOC_DIRSHIFT ) |  ( ord( $_[1] ) << &_IOC_TYPESHIFT ) |
43     ( $_[2] << &_IOC_NRSHIFT ) | ( $_[3] << &_IOC_SIZESHIFT );
44 }
45
46 sub _IO ($$)    { &_IOC( &_IOC_NONE,               $_[0], $_[1], 0 ) }
47 sub _IOR ($$$)  { &_IOC( &_IOC_READ,               $_[0], $_[1], $_[2] ) }
48 sub _IOW ($$$)  { &_IOC( &_IOC_WRITE,              $_[0], $_[1], $_[2] ) }
49 sub _IOWR ($$$) { &_IOC( &_IOC_READ | &_IOC_WRITE, $_[0], $_[1], $_[2] ) }
50
51 sub FIOC_GET_SIZE { _IOR('E', 0, 4); }
52 sub FIOC_SET_SIZE { _IOW('E', 1, 4); }
53
54 sub fioc_resize {
55     my ($size) = @_;
56     print 'called ', (caller(0))[3], "\n";
57     return 0 if $size == $fioc_size;
58     
59     if ($size < $fioc_size) {
60         $fioc_buf = substr($fioc_buf, 0, $size);
61     }
62     else {
63         $fioc_buf .= "\0" x ($size - $fioc_size);
64     }
65     $fioc_size = $size;
66     return 0;
67 }
68
69 sub fioc_expand {
70     my ($size) = @_;
71     print 'called ', (caller(0))[3], "\n";
72     if ($size > $fioc_size) {
73         return fioc_resize($size);
74     }
75     return 0;
76 }
77
78 sub fioc_file_type {
79     my ($path) = @_;
80     print 'called ', (caller(0))[3], "\n";
81     return FIOC_ROOT if $path eq '/';
82     return FIOC_FILE if $path eq '/' . FIOC_NAME;
83     return FIOC_NONE;
84 }
85
86 sub fioc_getattr {
87     my ($path) = @_;
88     print 'called ', (caller(0))[3], "\n";
89     my @stbuf = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
90
91     $stbuf[4] = $<;
92     $stbuf[5] = (split(/\s+/, $())[0];
93     $stbuf[8] = $stbuf[9] = time();
94
95     my $type = fioc_file_type($path);
96     if ($type == FIOC_ROOT) {
97         $stbuf[2] = S_IFDIR | 0755;
98         $stbuf[3] = 2;
99     }
100     elsif ($type == FIOC_FILE) {
101         $stbuf[2] = S_IFREG | 0644;
102         $stbuf[3] = 1;
103         $stbuf[7] = $fioc_size;
104     }
105     else {
106         return -&ENOENT;
107     }
108     return @stbuf;
109 }
110
111 sub fioc_open {
112     my ($path, $flags, $info) = @_;
113     print 'called ', (caller(0))[3], "\n";
114
115     if (fioc_file_type($path) != FIOC_NONE) {
116         return 0;
117     }
118     return -&ENOENT;
119 }
120
121 sub fioc_read {
122     my ($path, $size, $offset) = @_;
123     print 'called ', (caller(0))[3], "\n";
124
125     return -&EINVAL if fioc_file_type($path) != FIOC_FILE;
126
127     if ($offset > $fioc_size) {
128         return q{};
129     }
130
131     if ($size > $fioc_size - $offset) {
132         $size - $fioc_size - $offset;
133     }
134
135     return substr($fioc_buf, $offset, $size);
136 }
137
138 sub fioc_write {
139     my ($path, $data, $offset) = @_;
140     print 'called ', (caller(0))[3], "\n";
141
142     return -&EINVAL if fioc_file_type($path) != FIOC_FILE;
143
144     if (fioc_expand($offset + length($data))) {
145         return -&ENOMEM;
146     }
147
148     substr($fioc_buf, $offset, length($data), $data);
149     return length($data);
150 }
151
152 sub fioc_truncate {
153     my ($path, $size) = @_;
154     print 'called ', (caller(0))[3], "\n";
155
156     return -&EINVAL if fioc_file_type($path) != FIOC_FILE;
157
158     return fioc_resize($size);
159 }
160
161 sub fioc_readdir {
162     my ($path, $offset) = @_;
163     print 'called ', (caller(0))[3], "\n";
164
165     return -&EINVAL if fioc_file_type($path) != FIOC_ROOT;
166
167     return ('.', '..', FIOC_NAME, 0);
168 }
169
170 sub fioc_ioctl {
171     my ($path, $cmd, $flags, $data) = @_;
172     print 'called ', (caller(0))[3], "\n";
173     $cmd = unpack('L', pack('l', $cmd));
174
175     print("fioc_ioctl(): path is \"$path\", cmd is $cmd, flags is $flags\n");
176     return -&EINVAL if fioc_file_type($path) != FIOC_FILE;
177
178     return -&ENOSYS if $flags & 0x1;
179
180     if ($cmd == FIOC_GET_SIZE) {
181         print "handling FIOC_GET_SIZE\n";
182         return(0, pack('L', $fioc_size));
183     }
184     elsif ($cmd == FIOC_SET_SIZE) {
185         print "handling FIOC_SET_SIZE\n";
186         fioc_resize(unpack('L', $data));
187         return 0;
188     }
189
190     return -&EINVAL;
191 }
192
193 croak("Fuse doesn't have ioctl") unless Fuse::fuse_version() >= 2.8;
194
195 Fuse::main(
196     'mountpoint' => $ARGV[0],
197     'getattr'   => 'main::fioc_getattr',
198     'readdir'   => 'main::fioc_readdir',
199     'truncate'  => 'main::fioc_truncate',
200     'open'      => 'main::fioc_open',
201     'read'      => 'main::fioc_read',
202     'write'     => 'main::fioc_write',
203     'ioctl'     => 'main::fioc_ioctl');