rfusefs 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -13
- data/Gemfile +3 -1
- data/History.rdoc +6 -0
- data/README.rdoc +4 -5
- data/lib/fuse/fusedir.rb +15 -0
- data/lib/fuse/rfusefs-fuse.rb +446 -414
- data/lib/fusefs/metadir.rb +5 -5
- data/lib/fusefs/pathmapper.rb +7 -7
- data/lib/fusefs/sqlitemapper.rb +7 -2
- data/lib/rfusefs.rb +20 -48
- data/lib/rfusefs/version.rb +1 -1
- data/rfusefs.gemspec +2 -2
- data/samples/demo.rb +2 -2
- data/spec/metadir_spec.rb +280 -275
- data/spec/mount_unmount_spec.rb +2 -2
- data/spec/pathmapper_spec.rb +79 -79
- data/spec/rfusefs_spec.rb +505 -497
- data/spec/sample_spec.rb +12 -9
- data/spec/spec_helper.rb +3 -3
- data/spec/sqlitemapper_spec.rb +12 -12
- metadata +26 -27
- data/.travis.yml +0 -8
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
YWRhODkyYzJjZGNjYzc2YzM4ZTc5NjJhYWUyNjlkM2FiYzNjMGI4Nw==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d1a003ef4960543e188b2769eeb4cfac4feeb4db
|
4
|
+
data.tar.gz: a2571557c22e600005630de7ae3bede8c47771a6
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
MWU1NTI5M2IyMjU2Y2Q2MjU3ZTQwNzcwN2EzYmQ2MzAwNzI1ZmFlNWJlYTll
|
11
|
-
Y2FkMDY5NmNkYThjNmZhYWUzMDQ2ZWQ4YmRkMmMwNTRkNzYxMWY=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
OTgxN2NmMWQ3MjBlZTQwODU4NTBlMTdiNjQ3NTMwNTZiYjUzMjUxMTZhZDg4
|
14
|
-
MTllMGUwODliMGJjMDkzNTQwNDhiYTZhNzUzMzg0MjNiNmRjZWUwNGE1Yjdh
|
15
|
-
NWY2NmE2YzYwNDg4NWJhNGRmYzI5MjZiYjhjZjZhNzUwOTZiZWM=
|
6
|
+
metadata.gz: e98dee6386ba464801fd2eb79842cb88c82dcca79295669ad7f383ed00eb62a77eef43dc185c1d6265cd47f7a1407dc0c72817924a548fbc6faa2d8a86083b22
|
7
|
+
data.tar.gz: 1e03e2e67822f6472f60de58fc86de15a0e1a70251d62989b5212c3098f4a3cee942fdb6f33e7bb14446335799f41ddb6466a205cd22bc36d2084cd9c2290c8d
|
data/Gemfile
CHANGED
data/History.rdoc
CHANGED
data/README.rdoc
CHANGED
@@ -29,8 +29,7 @@ See samples under /samples and also the following starter classes
|
|
29
29
|
|
30
30
|
Then start your filesystem with
|
31
31
|
|
32
|
-
* {FuseFS.start}
|
33
|
-
* {FuseFS.main}
|
32
|
+
* {FuseFS.main} or {FuseFS.start}
|
34
33
|
|
35
34
|
Finally to use the filesystem open up your favourite file browser/terminal and
|
36
35
|
explore the contents under <mountpoint>
|
@@ -62,9 +61,9 @@ Happy Filesystem Hacking!
|
|
62
61
|
|
63
62
|
== REQUIREMENTS:
|
64
63
|
|
65
|
-
* FUSE (http://fuse.sourceforge.
|
64
|
+
* FUSE (http://fuse.sourceforge.net)
|
66
65
|
* Ruby (>=1.9)
|
67
|
-
* rfuse (~> 1.
|
66
|
+
* rfuse (~> 1.1)
|
68
67
|
|
69
68
|
== INSTALL:
|
70
69
|
|
@@ -85,7 +84,7 @@ After checking out the source, run:
|
|
85
84
|
|
86
85
|
* Copyright (c) 2005 Greg Millam. (FuseFS)
|
87
86
|
* Copyright (c) 2009 Kyle Maxwell. (FuseFS)
|
88
|
-
* Copyright (c) 2012 Grant Gardner. (RFuseFS)
|
87
|
+
* Copyright (c) 2012 - 2014 Grant Gardner. (RFuseFS)
|
89
88
|
|
90
89
|
Permission is hereby granted, free of charge, to any person obtaining
|
91
90
|
a copy of this software and associated documentation files (the
|
data/lib/fuse/fusedir.rb
CHANGED
@@ -120,7 +120,21 @@ module FuseFS
|
|
120
120
|
# * {#file?}(from), {#can_write?}(to), {#can_delete?}(from) and if all true
|
121
121
|
# * {#read_file}(from), {#write_to}(to), {#delete}(from)
|
122
122
|
# * otherwise reject the rename
|
123
|
+
#
|
124
|
+
# === Signals
|
125
|
+
#
|
126
|
+
# The filesystem can handle a signal by providing a `sig<name>` method. eg 'sighup'
|
127
|
+
# {#sigint} and {#sigterm} are handled by default to provide a means to exit the filesystem
|
123
128
|
class FuseDir
|
129
|
+
|
130
|
+
# @!method sigint()
|
131
|
+
# @return [void]
|
132
|
+
# Handle the INT signal and exit the filesystem
|
133
|
+
|
134
|
+
# @!method sigterm()
|
135
|
+
# @return [void]
|
136
|
+
# Handle the TERM signal and exit the filesystem
|
137
|
+
|
124
138
|
INIT_TIMES = Array.new(3,0)
|
125
139
|
|
126
140
|
# base,rest = split_path(path)
|
@@ -307,6 +321,7 @@ module FuseFS
|
|
307
321
|
# @return [void]
|
308
322
|
def unmounted();end
|
309
323
|
|
324
|
+
|
310
325
|
end
|
311
326
|
|
312
327
|
DEFAULT_FS = FuseDir.new()
|
data/lib/fuse/rfusefs-fuse.rb
CHANGED
@@ -1,508 +1,540 @@
|
|
1
1
|
# RFuseFS - FuseFS over RFuse
|
2
2
|
require 'rfuse'
|
3
3
|
require 'fcntl'
|
4
|
+
require 'forwardable'
|
4
5
|
|
5
6
|
module FuseFS
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
7
|
+
#Which raw api should we use?
|
8
|
+
RFUSEFS_COMPATIBILITY = true unless FuseFS.const_defined?(:RFUSEFS_COMPATIBILITY)
|
9
|
+
|
10
|
+
class FileHandle
|
11
|
+
@@fh = 0
|
12
|
+
attr_reader :id,:flags,:path
|
13
|
+
attr_accessor :raw,:contents
|
14
|
+
def initialize(path,flags)
|
15
|
+
@id = (@@fh += 1)
|
16
|
+
@flags = flags
|
17
|
+
@path = path
|
18
|
+
@modified = false
|
19
|
+
@contents = ""
|
20
|
+
@size = 0
|
21
|
+
end
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
def read(offset,size)
|
24
|
+
contents[offset,size]
|
25
|
+
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
def create
|
28
|
+
@contents = ""
|
29
|
+
@modified = true
|
30
|
+
end
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
32
|
+
def write(offset,data)
|
33
|
+
# TODO: why append?
|
34
|
+
if append? || offset >= contents.length
|
35
|
+
#ignore offset
|
36
|
+
#TODO: should this zero fill?
|
37
|
+
contents << data
|
38
|
+
else
|
39
|
+
contents[offset,data.length]=data
|
40
|
+
end
|
41
|
+
@modified = true
|
42
|
+
return data.length
|
43
|
+
end
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
45
|
+
def flush
|
46
|
+
@modified = false
|
47
|
+
contents
|
48
|
+
end
|
48
49
|
|
49
|
-
|
50
|
-
|
51
|
-
|
50
|
+
def modified?
|
51
|
+
@modified
|
52
|
+
end
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
54
|
+
def accmode
|
55
|
+
flags & Fcntl::O_ACCMODE
|
56
|
+
end
|
56
57
|
|
57
|
-
|
58
|
-
|
59
|
-
|
58
|
+
def rdwr?
|
59
|
+
accmode == Fcntl::O_RDWR
|
60
|
+
end
|
60
61
|
|
61
|
-
|
62
|
-
|
63
|
-
|
62
|
+
def wronly?
|
63
|
+
accmode == Fcntl::O_WRONLY
|
64
|
+
end
|
64
65
|
|
65
|
-
|
66
|
-
|
67
|
-
|
66
|
+
def rdonly?
|
67
|
+
accmode == Fcntl::O_RDONLY
|
68
|
+
end
|
68
69
|
|
69
|
-
|
70
|
-
|
71
|
-
|
70
|
+
def append?
|
71
|
+
writing? && (flags & Fcntl::O_APPEND != 0)
|
72
|
+
end
|
72
73
|
|
73
|
-
|
74
|
-
|
75
|
-
|
74
|
+
def reading?
|
75
|
+
rdonly? || rdwr?
|
76
|
+
end
|
76
77
|
|
77
|
-
|
78
|
-
|
79
|
-
|
78
|
+
def writing?
|
79
|
+
wronly? || rdwr?
|
80
|
+
end
|
80
81
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
82
|
+
def raw_mode
|
83
|
+
mode_str = case accmode
|
84
|
+
when Fcntl::O_RDWR; "rw"
|
85
|
+
when Fcntl::O_RDONLY; "r"
|
86
|
+
when Fcntl::O_WRONLY; "w"
|
87
|
+
end
|
87
88
|
|
88
|
-
|
89
|
-
|
90
|
-
end
|
89
|
+
mode_str << "a" if append?
|
90
|
+
return mode_str
|
91
91
|
end
|
92
|
+
end
|
92
93
|
|
93
|
-
|
94
|
+
# The real RFuse::Fuse object used by FuseFS
|
95
|
+
class Fuse < RFuse::FuseDelegator
|
96
|
+
def initialize(fs,*args)
|
97
|
+
@fs = fs
|
98
|
+
super
|
99
|
+
end
|
100
|
+
|
101
|
+
def run
|
102
|
+
begin
|
103
|
+
@fs.mounted
|
104
|
+
super
|
105
|
+
ensure
|
106
|
+
@fs.unmounted
|
107
|
+
end if mounted?
|
108
|
+
end
|
109
|
+
|
110
|
+
# Implements RFuseFS via RFuse
|
94
111
|
# The path supplied to these methods is generally validated by FUSE itself
|
95
112
|
# with a prior "getattr" call so we do not revalidate here.
|
96
113
|
# http://sourceforge.net/apps/mediawiki/fuse/index.php?title=FuseInvariants
|
97
|
-
class
|
98
|
-
CHECK_FILE="/._rfuse_check_"
|
99
|
-
|
100
|
-
def initialize(root)
|
101
|
-
@root = root
|
102
|
-
@created_files = { }
|
103
|
-
|
104
|
-
# Keep track of changes to file counts and sizes made via Fuse - for #statfs
|
105
|
-
@adj_nodes = 0
|
106
|
-
@adj_size = 0
|
107
|
-
|
108
|
-
#Define method missing for our filesystem
|
109
|
-
#so we can just call all the API methods as required.
|
110
|
-
def @root.method_missing(method,*args)
|
111
|
-
# our filesystem might implement method_missing itself
|
112
|
-
super
|
113
|
-
rescue NoMethodError
|
114
|
-
DEFAULT_FS.send(method,*args)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def readdir(ctx,path,filler,offset,ffi)
|
114
|
+
class Root
|
119
115
|
|
120
|
-
|
116
|
+
# We forward some methods
|
117
|
+
include Forwardable
|
121
118
|
|
122
|
-
|
123
|
-
filler.push(".",nil,0)
|
124
|
-
filler.push("..",nil,0)
|
119
|
+
CHECK_FILE="/._rfuse_check_"
|
125
120
|
|
126
|
-
|
121
|
+
def initialize(root)
|
122
|
+
@root = root
|
123
|
+
@created_files = { }
|
127
124
|
|
128
|
-
|
129
|
-
|
130
|
-
|
125
|
+
# Keep track of changes to file counts and sizes made via Fuse - for #statfs
|
126
|
+
@adj_nodes = 0
|
127
|
+
@adj_size = 0
|
131
128
|
|
129
|
+
#Define method missing for our filesystem
|
130
|
+
#so we can just call all the API methods as required.
|
131
|
+
def @root.method_missing(method,*args)
|
132
|
+
# our filesystem might implement method_missing itself
|
133
|
+
super
|
134
|
+
rescue NoMethodError
|
135
|
+
DEFAULT_FS.send(method,*args)
|
132
136
|
end
|
133
137
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
uid = Process.uid
|
139
|
-
gid = Process.gid
|
140
|
-
|
141
|
-
if path == "/" || @root.directory?(path)
|
142
|
-
#set "w" flag based on can_mkdir? || can_write? to path + "/._rfuse_check"
|
143
|
-
write_test_path = (path == "/" ? "" : path) + CHECK_FILE
|
144
|
-
|
145
|
-
mode = (@root.can_mkdir?(write_test_path) || @root.can_write?(write_test_path)) ? 0777 : 0555
|
146
|
-
atime,mtime,ctime = @root.times(path)
|
147
|
-
#nlink is set to 1 because apparently this makes find work.
|
148
|
-
return RFuse::Stat.directory(mode,{ :uid => uid, :gid => gid, :nlink => 1, :atime => atime, :mtime => mtime, :ctime => ctime })
|
149
|
-
elsif @created_files.has_key?(path)
|
150
|
-
return @created_files[path]
|
151
|
-
elsif @root.file?(path)
|
152
|
-
#Set mode from can_write and executable
|
153
|
-
mode = 0444
|
154
|
-
mode |= 0222 if @root.can_write?(path)
|
155
|
-
mode |= 0111 if @root.executable?(path)
|
156
|
-
size = size(path)
|
157
|
-
atime,mtime,ctime = @root.times(path)
|
158
|
-
return RFuse::Stat.file(mode,{ :uid => uid, :gid => gid, :size => size, :atime => atime, :mtime => mtime, :ctime => ctime })
|
159
|
-
else
|
160
|
-
raise Errno::ENOENT.new(path)
|
161
|
-
end
|
138
|
+
# Define sig<name> methods to handle signals
|
139
|
+
sigmethods = Signal.list.keys.map { |sn| "sig#{sn.downcase}".to_sym }.select { |sm| @root.respond_to?(sm) }
|
140
|
+
def_delegators(:@root,*sigmethods)
|
141
|
+
end
|
162
142
|
|
163
|
-
|
143
|
+
def readdir(ctx,path,filler,offset,ffi)
|
164
144
|
|
165
|
-
|
145
|
+
return wrap_context(ctx,__method__,path,filler,offset,ffi) if ctx
|
166
146
|
|
167
|
-
|
147
|
+
#Always have "." and ".."
|
148
|
+
filler.push(".",nil,0)
|
149
|
+
filler.push("..",nil,0)
|
168
150
|
|
169
|
-
|
170
|
-
raise Errno::EACCES.new(path)
|
171
|
-
end
|
151
|
+
files = @root.contents(path)
|
172
152
|
|
173
|
-
|
174
|
-
|
175
|
-
end
|
176
|
-
|
177
|
-
def mknod(ctx,path,mode,major,minor)
|
178
|
-
|
179
|
-
return wrap_context(ctx,__method__,path,mode,major,minor) if ctx
|
180
|
-
|
181
|
-
unless ((RFuse::Stat::S_IFMT & mode) == RFuse::Stat::S_IFREG ) && @root.can_write?(path)
|
182
|
-
raise Errno::EACCES.new(path)
|
183
|
-
end
|
153
|
+
files.each do | filename |
|
154
|
+
filler.push(filename,nil,0)
|
155
|
+
end
|
184
156
|
|
185
|
-
|
186
|
-
stat = RFuse::Stat.file(mode,{ :uid => Process.uid, :gid => Process.gid, :atime => now, :mtime => now, :ctime => now })
|
157
|
+
end
|
187
158
|
|
188
|
-
|
189
|
-
@adj_nodes += 1
|
190
|
-
end #mknod
|
159
|
+
def getattr(ctx,path)
|
191
160
|
|
192
|
-
|
193
|
-
#sizes are adjusted at file close
|
194
|
-
def ftruncate(ctx,path,offset,ffi)
|
161
|
+
return wrap_context(ctx,__method__,path) if ctx
|
195
162
|
|
196
|
-
|
163
|
+
uid = Process.uid
|
164
|
+
gid = Process.gid
|
197
165
|
|
198
|
-
|
166
|
+
if path == "/" || @root.directory?(path)
|
167
|
+
#set "w" flag based on can_mkdir? || can_write? to path + "/._rfuse_check"
|
168
|
+
write_test_path = (path == "/" ? "" : path) + CHECK_FILE
|
199
169
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
170
|
+
mode = (@root.can_mkdir?(write_test_path) || @root.can_write?(write_test_path)) ? 0777 : 0555
|
171
|
+
atime,mtime,ctime = @root.times(path)
|
172
|
+
#nlink is set to 1 because apparently this makes find work.
|
173
|
+
return RFuse::Stat.directory(mode,{ :uid => uid, :gid => gid, :nlink => 1, :atime => atime, :mtime => mtime, :ctime => ctime })
|
174
|
+
elsif @created_files.has_key?(path)
|
175
|
+
return @created_files[path]
|
176
|
+
elsif @root.file?(path)
|
177
|
+
#Set mode from can_write and executable
|
178
|
+
mode = 0444
|
179
|
+
mode |= 0222 if @root.can_write?(path)
|
180
|
+
mode |= 0111 if @root.executable?(path)
|
181
|
+
size = size(path)
|
182
|
+
atime,mtime,ctime = @root.times(path)
|
183
|
+
return RFuse::Stat.file(mode,{ :uid => uid, :gid => gid, :size => size, :atime => atime, :mtime => mtime, :ctime => ctime })
|
184
|
+
else
|
185
|
+
raise Errno::ENOENT.new(path)
|
208
186
|
end
|
209
187
|
|
210
|
-
|
211
|
-
def truncate(ctx,path,offset)
|
212
|
-
return wrap_context(ctx,__method__,path,offset) if ctx
|
213
|
-
|
214
|
-
unless @root.can_write?(path)
|
215
|
-
raise Errno::EACESS.new(path)
|
216
|
-
end
|
188
|
+
end #getattr
|
217
189
|
|
218
|
-
|
219
|
-
unless @root.raw_truncate(path,offset)
|
220
|
-
contents = @root.read_file(path)
|
221
|
-
if (offset <= 0)
|
222
|
-
@root.write_to(path,"")
|
223
|
-
elsif offset < contents.length
|
224
|
-
@root.write_to(path,contents[0..offset] )
|
225
|
-
end
|
226
|
-
end
|
227
|
-
@adj_size = @adj_size - current_size + (offset <= 0 ? 0 : offset)
|
228
|
-
end #truncate
|
229
|
-
|
230
|
-
# Open. Create a FileHandler and store in fuse file info
|
231
|
-
# This will be returned to us in read/write
|
232
|
-
# No O_CREATE (mknod first?), no O_TRUNC (truncate first)
|
233
|
-
def open(ctx,path,ffi)
|
234
|
-
return wrap_context(ctx,__method__,path,ffi) if ctx
|
235
|
-
fh = FileHandle.new(path,ffi.flags)
|
236
|
-
|
237
|
-
#Save the value return from raw_open to be passed back in raw_read/write etc..
|
238
|
-
if (FuseFS::RFUSEFS_COMPATIBILITY)
|
239
|
-
fh.raw = @root.raw_open(path,fh.raw_mode,true)
|
240
|
-
else
|
241
|
-
fh.raw = @root.raw_open(path,fh.raw_mode)
|
242
|
-
end
|
190
|
+
def mkdir(ctx,path,mode)
|
243
191
|
|
244
|
-
|
245
|
-
if fh.rdonly?
|
246
|
-
fh.contents = @root.read_file(path)
|
247
|
-
elsif fh.writing?
|
248
|
-
unless @root.can_write?(path)
|
249
|
-
raise Errno::EACCES.new(path)
|
250
|
-
end
|
251
|
-
|
252
|
-
if @created_files.has_key?(path)
|
253
|
-
fh.create
|
254
|
-
else
|
255
|
-
if fh.rdwr? || fh.append?
|
256
|
-
fh.contents = @root.read_file(path)
|
257
|
-
else #wronly && !append
|
258
|
-
#We should get a truncate 0, but might as well play it safe
|
259
|
-
fh.contents = ""
|
260
|
-
end
|
261
|
-
end
|
262
|
-
else
|
263
|
-
raise Errno::ENOPERM.new(path)
|
264
|
-
end
|
265
|
-
end
|
192
|
+
return wrap_context(ctx,__method__,path,mode) if ctx
|
266
193
|
|
267
|
-
|
268
|
-
|
194
|
+
unless @root.can_mkdir?(path)
|
195
|
+
raise Errno::EACCES.new(path)
|
269
196
|
end
|
270
197
|
|
271
|
-
|
272
|
-
|
198
|
+
@root.mkdir(path)
|
199
|
+
@adj_nodes += 1
|
200
|
+
end #mkdir
|
273
201
|
|
274
|
-
|
202
|
+
def mknod(ctx,path,mode,major,minor)
|
275
203
|
|
276
|
-
|
277
|
-
if FuseFS::RFUSEFS_COMPATIBILITY
|
278
|
-
return @root.raw_read(path,offset,size,fh.raw)
|
279
|
-
else
|
280
|
-
return @root.raw_read(path,offset,size)
|
281
|
-
end
|
282
|
-
elsif offset >= 0
|
283
|
-
return fh.read(offset,size)
|
284
|
-
else
|
285
|
-
#TODO: Raise? what does a negative offset mean
|
286
|
-
return ""
|
287
|
-
end
|
288
|
-
rescue EOFError
|
289
|
-
return ""
|
290
|
-
end
|
204
|
+
return wrap_context(ctx,__method__,path,mode,major,minor) if ctx
|
291
205
|
|
292
|
-
|
293
|
-
|
294
|
-
fh = ffi.fh
|
295
|
-
|
296
|
-
if fh.raw
|
297
|
-
if FuseFS::RFUSEFS_COMPATIBILITY
|
298
|
-
return @root.raw_write(path,offset,buf.length,buf,fh.raw)
|
299
|
-
else
|
300
|
-
@root.raw_write(path,offset,buf.length,buf)
|
301
|
-
return buf.length
|
302
|
-
end
|
303
|
-
else
|
304
|
-
return fh.write(offset,buf)
|
305
|
-
end
|
206
|
+
unless ((RFuse::Stat::S_IFMT & mode) == RFuse::Stat::S_IFREG ) && @root.can_write?(path)
|
207
|
+
raise Errno::EACCES.new(path)
|
306
208
|
end
|
307
209
|
|
308
|
-
|
309
|
-
|
310
|
-
fh = ffi.fh
|
210
|
+
now = Time.now
|
211
|
+
stat = RFuse::Stat.file(mode,{ :uid => Process.uid, :gid => Process.gid, :atime => now, :mtime => now, :ctime => now })
|
311
212
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
else
|
316
|
-
@root.raw_sync(path,datasync != 0)
|
317
|
-
end
|
318
|
-
else
|
319
|
-
flush(nil,path,ffi)
|
320
|
-
end
|
321
|
-
end
|
213
|
+
@created_files[path] = stat
|
214
|
+
@adj_nodes += 1
|
215
|
+
end #mknod
|
322
216
|
|
323
|
-
|
324
|
-
|
325
|
-
|
217
|
+
#ftruncate - eg called after opening a file for write without append
|
218
|
+
#sizes are adjusted at file close
|
219
|
+
def ftruncate(ctx,path,offset,ffi)
|
326
220
|
|
327
|
-
|
328
|
-
#write contents to the file and mark it unmodified
|
329
|
-
@root.write_to(path,fh.flush())
|
330
|
-
#if it was created with mknod it now exists in the filesystem...
|
331
|
-
@created_files.delete(path)
|
332
|
-
end
|
333
|
-
end
|
221
|
+
return wrap_context(ctx,__method__,path,offset,ffi) if ctx
|
334
222
|
|
335
|
-
|
336
|
-
return wrap_context(ctx,__method__,path,ffi) if ctx
|
223
|
+
fh = ffi.fh
|
337
224
|
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
end
|
346
|
-
# if was handled as raw, then assume the file has now been created (or not)
|
347
|
-
@created_files.delete(path)
|
348
|
-
else
|
349
|
-
# Probably just had flush called, but no harm calling it again
|
350
|
-
flush(nil,path,ffi)
|
351
|
-
end
|
225
|
+
if fh.raw
|
226
|
+
@root.raw_truncate(path,offset,fh.raw)
|
227
|
+
if (offset <= 0)
|
228
|
+
fh.contents = ""
|
229
|
+
else
|
230
|
+
fh.contents = fh.contents[0..offset]
|
231
|
+
end
|
352
232
|
end
|
233
|
+
end
|
353
234
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
#def chown(path,uid,gid)
|
358
|
-
#end
|
359
|
-
|
360
|
-
def utime(ctx,path,actime,modtime)
|
361
|
-
return wrap_context(ctx,__method__,path,actime,modtime) if ctx
|
235
|
+
#truncate a file outside of open files
|
236
|
+
def truncate(ctx,path,offset)
|
237
|
+
return wrap_context(ctx,__method__,path,offset) if ctx
|
362
238
|
|
363
|
-
|
364
|
-
|
239
|
+
unless @root.can_write?(path)
|
240
|
+
raise Errno::EACESS.new(path)
|
365
241
|
end
|
366
242
|
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
@created_files.delete(path)
|
377
|
-
@root.delete(path)
|
243
|
+
current_size = size(path)
|
244
|
+
unless @root.raw_truncate(path,offset)
|
245
|
+
contents = @root.read_file(path)
|
246
|
+
if (offset <= 0)
|
247
|
+
@root.write_to(path,"")
|
248
|
+
elsif offset < contents.length
|
249
|
+
@root.write_to(path,contents[0..offset] )
|
250
|
+
end
|
378
251
|
end
|
252
|
+
@adj_size = @adj_size - current_size + (offset <= 0 ? 0 : offset)
|
253
|
+
end #truncate
|
379
254
|
|
380
|
-
|
381
|
-
|
255
|
+
# Open. Create a FileHandler and store in fuse file info
|
256
|
+
# This will be returned to us in read/write
|
257
|
+
# No O_CREATE (mknod first?), no O_TRUNC (truncate first)
|
258
|
+
def open(ctx,path,ffi)
|
259
|
+
return wrap_context(ctx,__method__,path,ffi) if ctx
|
260
|
+
fh = FileHandle.new(path,ffi.flags)
|
382
261
|
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
262
|
+
#Save the value return from raw_open to be passed back in raw_read/write etc..
|
263
|
+
if (FuseFS::RFUSEFS_COMPATIBILITY)
|
264
|
+
fh.raw = @root.raw_open(path,fh.raw_mode,true)
|
265
|
+
else
|
266
|
+
fh.raw = @root.raw_open(path,fh.raw_mode)
|
387
267
|
end
|
388
268
|
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
269
|
+
unless fh.raw
|
270
|
+
if fh.rdonly?
|
271
|
+
fh.contents = @root.read_file(path)
|
272
|
+
elsif fh.writing?
|
273
|
+
unless @root.can_write?(path)
|
274
|
+
raise Errno::EACCES.new(path)
|
275
|
+
end
|
394
276
|
|
395
|
-
if @
|
396
|
-
|
397
|
-
elsif @root.file?(from) && @root.can_write?(to) && @root.can_delete?(from)
|
398
|
-
contents = @root.read_file(from)
|
399
|
-
@root.write_to(to,contents)
|
400
|
-
@root.delete(from)
|
277
|
+
if @created_files.has_key?(path)
|
278
|
+
fh.create
|
401
279
|
else
|
402
|
-
|
280
|
+
if fh.rdwr? || fh.append?
|
281
|
+
fh.contents = @root.read_file(path)
|
282
|
+
else #wronly && !append
|
283
|
+
#We should get a truncate 0, but might as well play it safe
|
284
|
+
fh.contents = ""
|
285
|
+
end
|
403
286
|
end
|
404
|
-
|
287
|
+
else
|
288
|
+
raise Errno::ENOPERM.new(path)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
#If we get this far, save our filehandle in the FUSE structure
|
293
|
+
ffi.fh=fh
|
294
|
+
end
|
295
|
+
|
296
|
+
def read(ctx,path,size,offset,ffi)
|
297
|
+
return wrap_context(ctx,__method__,path,size,offset,ffi) if ctx
|
298
|
+
|
299
|
+
fh = ffi.fh
|
405
300
|
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
return
|
411
|
-
|
301
|
+
if fh.raw
|
302
|
+
if FuseFS::RFUSEFS_COMPATIBILITY
|
303
|
+
return @root.raw_read(path,offset,size,fh.raw)
|
304
|
+
else
|
305
|
+
return @root.raw_read(path,offset,size)
|
306
|
+
end
|
307
|
+
elsif offset >= 0
|
308
|
+
return fh.read(offset,size)
|
309
|
+
else
|
310
|
+
#TODO: Raise? what does a negative offset mean
|
311
|
+
return ""
|
412
312
|
end
|
313
|
+
rescue EOFError
|
314
|
+
return ""
|
315
|
+
end
|
413
316
|
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
raise Errno::ENODATA.new("No attribute #{name}") unless result
|
418
|
-
result.to_s
|
419
|
-
end
|
317
|
+
def write(ctx,path,buf,offset,ffi)
|
318
|
+
return wrap_context(ctx,__method__,path,buf,offset,ffi) if ctx
|
319
|
+
fh = ffi.fh
|
420
320
|
|
421
|
-
|
422
|
-
|
423
|
-
@root.
|
321
|
+
if fh.raw
|
322
|
+
if FuseFS::RFUSEFS_COMPATIBILITY
|
323
|
+
return @root.raw_write(path,offset,buf.length,buf,fh.raw)
|
324
|
+
else
|
325
|
+
@root.raw_write(path,offset,buf.length,buf)
|
326
|
+
return buf.length
|
327
|
+
end
|
328
|
+
else
|
329
|
+
return fh.write(offset,buf)
|
424
330
|
end
|
331
|
+
end
|
425
332
|
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
end
|
333
|
+
def fsync(ctx,path,datasync,ffi)
|
334
|
+
return wrap_context(ctx,__method__,path,datasync,ffi) if ctx
|
335
|
+
fh = ffi.fh
|
430
336
|
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
#end
|
440
|
-
|
441
|
-
# Some random numbers to show with df command
|
442
|
-
# bsize preferred block size = 1K unless @root provides something different
|
443
|
-
# frsize = bsize (but apparently unused)
|
444
|
-
# blocks = total number of blocks
|
445
|
-
# bfree = number of free blocks
|
446
|
-
# bavail = bfree if mounted -o allow_other
|
447
|
-
# files = count of all files
|
448
|
-
# ffree - count of free file inode
|
449
|
-
#
|
450
|
-
def statfs(ctx,path)
|
451
|
-
return wrap_context(ctx,__method__,path) if ctx
|
452
|
-
block_size = 1024
|
453
|
-
|
454
|
-
stats = @root.statistics(path)
|
455
|
-
case stats
|
456
|
-
when Array
|
457
|
-
used_space, used_files, total_space, total_files = stats
|
458
|
-
used_files ||= 0
|
459
|
-
used_space ||= 0
|
460
|
-
total_files ||= used_files
|
461
|
-
total_space ||= used_space
|
462
|
-
result = RFuse::StatVfs.new(
|
463
|
-
"bsize" => block_size,
|
464
|
-
"frsize" => block_size,
|
465
|
-
"blocks" => total_space / block_size,
|
466
|
-
"bfree" => (total_space - used_space)/block_size,
|
467
|
-
"bavail" => (total_space - used_space)/block_size,
|
468
|
-
"files" => total_files,
|
469
|
-
"ffree" => (total_files - used_files)
|
470
|
-
)
|
471
|
-
return result
|
472
|
-
else
|
473
|
-
#expected to quack like rfuse:statvfs
|
474
|
-
return stats
|
475
|
-
end
|
337
|
+
if fh && fh.raw
|
338
|
+
if FuseFS::RFUSEFS_COMPATIBILITY
|
339
|
+
@root.raw_sync(path,datasync != 0,fh.raw)
|
340
|
+
else
|
341
|
+
@root.raw_sync(path,datasync != 0)
|
342
|
+
end
|
343
|
+
else
|
344
|
+
flush(nil,path,ffi)
|
476
345
|
end
|
477
|
-
|
478
|
-
|
346
|
+
end
|
347
|
+
|
348
|
+
def flush(ctx,path,ffi)
|
349
|
+
return wrap_context(ctx,__method__,path,ffi) if ctx
|
350
|
+
fh = ffi.fh
|
351
|
+
|
352
|
+
if fh && !fh.raw && fh.modified?
|
353
|
+
#write contents to the file and mark it unmodified
|
354
|
+
@root.write_to(path,fh.flush())
|
355
|
+
#if it was created with mknod it now exists in the filesystem...
|
356
|
+
@created_files.delete(path)
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
def release(ctx,path,ffi)
|
361
|
+
return wrap_context(ctx,__method__,path,ffi) if ctx
|
362
|
+
|
363
|
+
|
364
|
+
fh = ffi.fh
|
365
|
+
if fh && fh.raw
|
366
|
+
if (FuseFS::RFUSEFS_COMPATIBILITY)
|
367
|
+
@root.raw_close(path,fh.raw)
|
368
|
+
else
|
369
|
+
@root.raw_close(path)
|
370
|
+
end
|
371
|
+
# if was handled as raw, then assume the file has now been created (or not)
|
372
|
+
@created_files.delete(path)
|
373
|
+
else
|
374
|
+
# Probably just had flush called, but no harm calling it again
|
375
|
+
flush(nil,path,ffi)
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
#def chmod(path,mode)
|
380
|
+
#end
|
381
|
+
|
382
|
+
#def chown(path,uid,gid)
|
383
|
+
#end
|
384
|
+
|
385
|
+
def utime(ctx,path,actime,modtime)
|
386
|
+
return wrap_context(ctx,__method__,path,actime,modtime) if ctx
|
387
|
+
|
388
|
+
#Touch...
|
389
|
+
@root.touch(path,modtime) if @root.respond_to?(:touch)
|
390
|
+
end
|
391
|
+
|
392
|
+
def unlink(ctx,path)
|
393
|
+
return wrap_context(ctx,__method__,path) if ctx
|
394
|
+
|
395
|
+
unless @root.can_delete?(path)
|
396
|
+
raise Errno::EACCES.new(path)
|
397
|
+
end
|
398
|
+
|
399
|
+
@adj_size = @adj_size - size(path)
|
400
|
+
|
401
|
+
@created_files.delete(path)
|
402
|
+
@root.delete(path)
|
403
|
+
end
|
404
|
+
|
405
|
+
def rmdir(ctx,path)
|
406
|
+
return wrap_context(ctx,__method__,path) if ctx
|
407
|
+
|
408
|
+
unless @root.can_rmdir?(path)
|
409
|
+
raise Errno::EACCES.new(path)
|
410
|
+
end
|
411
|
+
@root.rmdir(path)
|
412
|
+
end
|
413
|
+
|
414
|
+
#def symlink(path,as)
|
415
|
+
#end
|
416
|
+
|
417
|
+
def rename(ctx,from,to)
|
418
|
+
return wrap_context(ctx,__method__,from,to) if ctx
|
419
|
+
|
420
|
+
if @root.rename(from,to)
|
421
|
+
# nothing to do
|
422
|
+
elsif @root.file?(from) && @root.can_write?(to) && @root.can_delete?(from)
|
423
|
+
contents = @root.read_file(from)
|
424
|
+
@root.write_to(to,contents)
|
425
|
+
@root.delete(from)
|
426
|
+
else
|
427
|
+
raise Errno::EACCES.new("Unable to move directory #{from}")
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
#def link(path,as)
|
432
|
+
#end
|
433
|
+
|
434
|
+
def setxattr(ctx,path,name,value,flags)
|
435
|
+
return wrap_context(ctx,__method__,path,name,value,flags) if ctx
|
436
|
+
@root.xattr(path)[name]=value
|
437
|
+
end
|
438
|
+
|
439
|
+
def getxattr(ctx,path,name)
|
440
|
+
return wrap_context(ctx,__method__,path,name) if ctx
|
441
|
+
result = @root.xattr(path)[name]
|
442
|
+
raise Errno::ENODATA.new("No attribute #{name}") unless result
|
443
|
+
result.to_s
|
444
|
+
end
|
445
|
+
|
446
|
+
def listxattr(ctx,path)
|
447
|
+
return wrap_context(ctx,__method__,path) if ctx
|
448
|
+
@root.xattr(path).keys
|
449
|
+
end
|
450
|
+
|
451
|
+
def removexattr(ctx,path,name)
|
452
|
+
return wrap_context(ctx,__method__,path,name) if ctx
|
453
|
+
@root.xattr(path).delete(name)
|
454
|
+
end
|
455
|
+
|
456
|
+
#def opendir(path,ffi)
|
457
|
+
#end
|
458
|
+
|
459
|
+
#def releasedir(path,ffi)
|
460
|
+
#end
|
461
|
+
|
462
|
+
#
|
463
|
+
#def fsyncdir(path,meta,ffi)
|
464
|
+
#end
|
465
|
+
|
466
|
+
# Some random numbers to show with df command
|
467
|
+
# bsize preferred block size = 1K unless @root provides something different
|
468
|
+
# frsize = bsize (but apparently unused)
|
469
|
+
# blocks = total number of blocks
|
470
|
+
# bfree = number of free blocks
|
471
|
+
# bavail = bfree if mounted -o allow_other
|
472
|
+
# files = count of all files
|
473
|
+
# ffree - count of free file inode
|
474
|
+
#
|
475
|
+
def statfs(ctx,path)
|
476
|
+
return wrap_context(ctx,__method__,path) if ctx
|
477
|
+
block_size = 1024
|
478
|
+
|
479
|
+
stats = @root.statistics(path)
|
480
|
+
case stats
|
481
|
+
when Array
|
482
|
+
used_space, used_files, total_space, total_files = stats
|
483
|
+
used_files ||= 0
|
484
|
+
used_space ||= 0
|
485
|
+
total_files ||= used_files
|
486
|
+
total_space ||= used_space
|
487
|
+
result = RFuse::StatVfs.new(
|
488
|
+
"bsize" => block_size,
|
489
|
+
"frsize" => block_size,
|
490
|
+
"blocks" => total_space / block_size,
|
491
|
+
"bfree" => (total_space - used_space)/block_size,
|
492
|
+
"bavail" => (total_space - used_space)/block_size,
|
493
|
+
"files" => total_files,
|
494
|
+
"ffree" => (total_files - used_files)
|
495
|
+
)
|
496
|
+
return result
|
497
|
+
else
|
498
|
+
#expected to quack like rfuse:statvfs
|
499
|
+
return stats
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
def mounted()
|
479
504
|
@root.mounted()
|
480
|
-
|
505
|
+
end
|
481
506
|
|
482
|
-
|
507
|
+
def unmounted()
|
483
508
|
@root.unmounted()
|
484
|
-
|
509
|
+
end
|
485
510
|
|
486
|
-
|
511
|
+
def self.context(ctx,&block)
|
487
512
|
begin
|
488
|
-
|
489
|
-
|
490
|
-
|
513
|
+
Thread.current[:fusefs_reader_uid] = ctx.uid
|
514
|
+
Thread.current[:fusefs_reader_gid] = ctx.gid
|
515
|
+
yield
|
491
516
|
ensure
|
492
|
-
|
493
|
-
|
517
|
+
Thread.current[:fusefs_reader_uid] = nil
|
518
|
+
Thread.current[:fusefs_reader_gid] = nil
|
494
519
|
end
|
495
|
-
|
520
|
+
end
|
521
|
+
|
522
|
+
# @private - no doc
|
523
|
+
def to_s
|
524
|
+
"RFuseFS::#{@root}"
|
525
|
+
end
|
496
526
|
|
497
|
-
|
527
|
+
private
|
498
528
|
|
499
|
-
|
529
|
+
def wrap_context(ctx,method,*args)
|
500
530
|
self.class.context(ctx) { send(method,nil,*args) }
|
501
|
-
|
531
|
+
end
|
502
532
|
|
503
|
-
|
533
|
+
def size(path)
|
504
534
|
@root.respond_to?(:size) ? @root.size(path) : @root.read_file(path).length
|
505
|
-
|
535
|
+
end
|
536
|
+
|
537
|
+
end #class Root
|
506
538
|
|
507
|
-
|
539
|
+
end #class Fuse
|
508
540
|
end #Module FuseFS
|