rfusefs 1.0.3 → 1.1.0.rc202009.34

Sign up to get free protection for your applications and to get access to all the features.
data/TODO.txt DELETED
@@ -1,6 +0,0 @@
1
-
2
- = TODO
3
-
4
- Fix (or remove) samples
5
-
6
-
@@ -1,328 +0,0 @@
1
- module FuseFS
2
-
3
- # Helper for filesystem accounting
4
- class StatsHelper
5
-
6
- # @return [Integer] size of filesystem in bytes
7
- attr_accessor :max_space
8
- # @return [Integer] maximum number of (virtual) inodes
9
- attr_accessor :max_nodes
10
-
11
- # If set true, adjustments that cause space/nodes to exceed
12
- # the maximums will raise ENOSPC (no space left on device)
13
- # @return [Boolean]
14
- attr_accessor :strict
15
-
16
- # @return [Integer] used space in bytes
17
- attr_reader :space
18
-
19
- # @return [Integer] used inodes (typically count of files and directories)
20
- attr_reader :nodes
21
-
22
- #
23
- # @param [Integer] max_space
24
- # @param [Integer] max_nodes
25
- # @param [Booleanr] strict
26
- def initialize(max_space=nil,max_nodes=nil,strict=false)
27
- @nodes = 0
28
- @space = 0
29
- @max_space = max_space
30
- @max_nodes = max_nodes
31
- @strict = strict
32
- end
33
-
34
- # Adjust accumlated statistics
35
- # @param [Integer] delta_space change in {#space} usage
36
- # @param [Integer] delta_nodes change in {#nodes} usage
37
- #
38
- # @return [void]
39
- # @raise [Errno::ENOSPC] if {#strict} and adjusted {#space}/{#nodes} would exceed {#max_space} or {#max_nodes}
40
- def adjust(delta_space,delta_nodes=0)
41
- @nodes += delta_nodes
42
- @space += delta_space
43
- raise Errno::ENOSPC if @strict && ( @nodes > @max_nodes || @space > @max_space )
44
- end
45
-
46
- # @overload to_statistics()
47
- # @return [Array<Integer>] in format expected by {FuseDir#statistics}
48
- # @overload to_statistics(free_space,free_nodes)
49
- # Calculate total space so that free space remains fixed
50
- # @param [Integer] free_space available space in bytes
51
- # @param [Integer] free_nodes available (virtual) inodes
52
- # @return [Array<Integer>] in format expected by {FuseDir#statistics}
53
- def to_statistics(free_space=nil,free_nodes=nil)
54
- total_space = free_space ? space + free_space : max_space
55
- total_nodes = free_nodes ? nodes + free_nodes : max_nodes
56
- [ @space, @nodes, total_space, total_nodes ]
57
- end
58
- end
59
-
60
- # This class is equivalent to using Object.new() as the virtual directory
61
- # for target for {FuseFS.start}. It exists primarily to document the API
62
- # but can also be used as a superclass for your filesystem providing sensible defaults
63
- #
64
- # == Method call sequences
65
- #
66
- # === Stat (getattr)
67
- #
68
- # FUSE itself will generally stat referenced files and validate the results
69
- # before performing any file/directory operations so this sequence is called
70
- # very often
71
- #
72
- # 1. {#directory?} is checked first
73
- # * {#can_write?} OR {#can_mkdir?} with .\_rfusefs_check\_ to determine write permissions
74
- # * {#times} is called to determine atime,mtime,ctime info for the directory
75
- #
76
- # 2. {#file?} is checked next
77
- # * {#can_write?}, {#executable?}, {#size}, {#times} are called to fill out the details
78
- #
79
- # 3. otherwise we tell FUSE that the path does not exist
80
- #
81
- # === List directory
82
- #
83
- # FUSE confirms the path is a directory (via stat above) before we call {#contents}
84
- #
85
- # FUSE will generally go on to stat each directory entry in the results
86
- #
87
- # === Reading files
88
- #
89
- # FUSE confirms path is a file before we call {#read_file}
90
- #
91
- # For fine control of file access see {#raw_open}, {#raw_read}, {#raw_close}
92
- #
93
- # === Writing files
94
- #
95
- # FUSE confirms path for the new file is a directory
96
- #
97
- # * {#can_write?} is checked at file open
98
- # * {#write_to} is called when the file is synced, flushed or closed
99
- #
100
- # See also {#raw_open}, {#raw_truncate}, {#raw_write}, {#raw_sync}, {#raw_close}
101
- #
102
- # === Deleting files
103
- #
104
- # FUSE confirms path is a file before we call {#can_delete?} then {#delete}
105
- #
106
- # === Creating directories
107
- #
108
- # FUSE confirms parent is a directory before we call {#can_mkdir?} then {#mkdir}
109
- #
110
- # === Deleting directories
111
- #
112
- # FUSE confirms path is a directory before we call {#can_rmdir?} then {#rmdir}
113
- #
114
- # === Renaming files and directories
115
- #
116
- # FUSE confirms the rename is valid (eg. not renaming a directory to a file)
117
- #
118
- # * Try {#rename} to see if the virtual directory wants to handle this itself
119
- # * If rename returns false/nil then we try to copy/delete (files only) ie.
120
- # * {#file?}(from), {#can_write?}(to), {#can_delete?}(from) and if all true
121
- # * {#read_file}(from), {#write_to}(to), {#delete}(from)
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
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
-
138
- INIT_TIMES = Array.new(3,0)
139
-
140
- # base,rest = split_path(path)
141
- # @return [Array<String,String>] base,rest. base is the first directory in
142
- # path, and rest is nil> or the remaining path.
143
- # Typically if rest is not nil? you should
144
- # recurse the paths
145
- def split_path(path)
146
- cur, *rest = path.scan(/[^\/]+/)
147
- if rest.empty?
148
- [ cur, nil ]
149
- else
150
- [ cur, File::SEPARATOR + File.join(rest) ]
151
- end
152
- end
153
-
154
- # base,*rest = scan_path(path)
155
- # @return [Array<String>] all directory and file elements in path. Useful
156
- # when encapsulating an entire fs into one object
157
- def scan_path(path)
158
- path.scan(/[^\/]+/)
159
- end
160
-
161
- # @abstract FuseFS api
162
- # @return [Boolean] true if path is a directory
163
- def directory?(path);return false;end
164
-
165
- # @abstract FuseFS api
166
- # @return [Boolean] true if path is a file
167
- def file?(path);end
168
-
169
- # @abstract FuseFS api
170
- # @return [Array<String>] array of file and directory names within path
171
- def contents(path);return [];end
172
-
173
- # @abstract FuseFS api
174
- # @return [Boolean] true if path is an executable file
175
- def executable?(path);return false;end
176
-
177
- # File size
178
- # @abstract FuseFS api
179
- # @return [Integer] the size in byte of a file (lots of applications rely on this being accurate )
180
- def size(path); read_file(path).length ;end
181
-
182
- # File time information. RFuseFS extension.
183
- # @abstract FuseFS api
184
- # @return [Array<Integer, Time>] a 3 element array [ atime, mtime. ctime ] (good for rsync etc)
185
- def times(path);return INIT_TIMES;end
186
-
187
- # @abstract FuseFS api
188
- # @return [String] the contents of the file at path
189
- def read_file(path);return "";end
190
-
191
- # @abstract FuseFS api
192
- # @return [Boolean] true if the user can write to file at path
193
- def can_write?(path);return false;end
194
-
195
- # Write the contents of str to file at path
196
- # @abstract FuseFS api
197
- # @return [void]
198
- def write_to(path,str);end
199
-
200
- # @abstract FuseFS api
201
- # @return [Boolean] true if the user can delete the file at path
202
- def can_delete?(path);return false;end
203
-
204
- # Delete the file at path
205
- # @abstract FuseFS api
206
- # @return [void]
207
- def delete(path);end
208
-
209
- # @abstract FuseFS api
210
- # @return [Boolean] true if user can make a directory at path
211
- def can_mkdir?(path);return false;end
212
-
213
- # Make a directory at path
214
- # @abstract FuseFS api
215
- # @return [void]
216
- def mkdir(path);end
217
-
218
- # @abstract FuseFS api
219
- # @return [Boolean] true if user can remove a directory at path
220
- def can_rmdir?(path);return false;end
221
-
222
- # Remove the directory at path
223
- # @abstract FuseFS api
224
- # @return [void]
225
- def rmdir(path);end
226
-
227
- # Neat toy. Called when a file is touched or has its timestamp explicitly modified
228
- # @abstract FuseFS api
229
- # @return [void]
230
- def touch(path,modtime);end
231
-
232
- # Move a file or directory.
233
- # @abstract FuseFS api
234
- # @return [Boolean] true to indicate the rename has been handled,
235
- # otherwise will fallback to copy/delete
236
- def rename(from_path,to_path);end
237
-
238
- # Raw file access
239
- # @abstract FuseFS api
240
- # @param mode [String] "r","w" or "rw", with "a" if file is opened for append
241
- # @param rfusefs [Boolean] will be "true" if RFuseFS extensions are available
242
- # @return [nil] to indicate raw operations are not implemented
243
- # @return [Object] a filehandle
244
- # Under RFuseFS this object will be passed back in to the other raw
245
- # methods as the optional parameter _raw_
246
- #
247
- def raw_open(path,mode,rfusefs = nil);end
248
-
249
- # RFuseFS extension.
250
- # @abstract FuseFS api
251
- #
252
- # @overload raw_truncate(path,offset,raw)
253
- # Truncate an open file to offset bytes
254
- # @param [String] path
255
- # @param [Integer] offset
256
- # @param [Object] raw the filehandle returned from {#raw_open}
257
- # @return [void]
258
- #
259
- # @overload raw_truncate(path,offset)
260
- # Optionally truncate a file to offset bytes directly
261
- # @param [String] path
262
- # @param [Integer] offset
263
- # @return [Boolean]
264
- # if truncate has been performed, otherwise the truncation will be performed with {#read_file} and {#write_to}
265
- #
266
- def raw_truncate(path,offset,raw=nil);end
267
-
268
- # Read _sz_ bytes from file at path (or filehandle raw) starting at offset off
269
- #
270
- # @param [String] path
271
- # @param [Integer] offset
272
- # @param [Integer] size
273
- # @param [Object] raw the filehandle returned by {#raw_open}
274
- # @abstract FuseFS api
275
- # @return [void]
276
- def raw_read(path,offset,size,raw=nil);end
277
-
278
- # Write _sz_ bytes from file at path (or filehandle raw) starting at offset off
279
- # @abstract FuseFS api
280
- # @return [void]
281
- def raw_write(path,off,sz,buf,raw=nil);end
282
-
283
-
284
- # Sync buffered data to your filesystem
285
- # @param [String] path
286
- # @param [Boolena] datasync only sync user data, not metadata
287
- # @param [Object] raw the filehandle return by {#raw_open}
288
- def raw_sync(path,datasync,raw=nil);end
289
-
290
- # Close the file previously opened at path (or filehandle raw)
291
- # @abstract FuseFS api
292
- # @return [void]
293
- def raw_close(path,raw=nil);end
294
-
295
- # RFuseFS extension.
296
- # Extended attributes.
297
- # @param [String] path
298
- # @return [Hash] extended attributes for this path.
299
- # The returned object will be manipulated directly using :[] :[]=,, :keys and :delete
300
- # so the default (a new empty hash on every call) will not retain attributes that are set
301
- # @abstract FuseFS api
302
- def xattr(path); {} ; end
303
-
304
- # RFuseFS extensions.
305
- # File system statistics
306
- # @param [String] path
307
- # @return [Array<Integer>] the statistics
308
- # used_space (in bytes), used_files, max_space, max_files
309
- # See {StatsHelper}
310
- # @return [RFuse::StatVfs] or raw statistics
311
- # @abstract FuseFS api
312
- def statistics(path); [0,0,0,0]; end
313
-
314
- # RFuseFS extension.
315
- # Called when the filesystem is mounted
316
- # @return [void]
317
- def mounted();end
318
-
319
- # RFuseFS extension.
320
- # Called when the filesystem is unmounted
321
- # @return [void]
322
- def unmounted();end
323
-
324
-
325
- end
326
-
327
- DEFAULT_FS = FuseDir.new()
328
- end
@@ -1,540 +0,0 @@
1
- # RFuseFS - FuseFS over RFuse
2
- require 'rfuse'
3
- require 'fcntl'
4
- require 'forwardable'
5
-
6
- module FuseFS
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
22
-
23
- def read(offset,size)
24
- contents[offset,size]
25
- end
26
-
27
- def create
28
- @contents = ""
29
- @modified = true
30
- end
31
-
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
44
-
45
- def flush
46
- @modified = false
47
- contents
48
- end
49
-
50
- def modified?
51
- @modified
52
- end
53
-
54
- def accmode
55
- flags & Fcntl::O_ACCMODE
56
- end
57
-
58
- def rdwr?
59
- accmode == Fcntl::O_RDWR
60
- end
61
-
62
- def wronly?
63
- accmode == Fcntl::O_WRONLY
64
- end
65
-
66
- def rdonly?
67
- accmode == Fcntl::O_RDONLY
68
- end
69
-
70
- def append?
71
- writing? && (flags & Fcntl::O_APPEND != 0)
72
- end
73
-
74
- def reading?
75
- rdonly? || rdwr?
76
- end
77
-
78
- def writing?
79
- wronly? || rdwr?
80
- end
81
-
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
88
-
89
- mode_str << "a" if append?
90
- return mode_str
91
- end
92
- end
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
111
- # The path supplied to these methods is generally validated by FUSE itself
112
- # with a prior "getattr" call so we do not revalidate here.
113
- # http://sourceforge.net/apps/mediawiki/fuse/index.php?title=FuseInvariants
114
- class Root
115
-
116
- # We forward some methods
117
- include Forwardable
118
-
119
- CHECK_FILE="/._rfuse_check_"
120
-
121
- def initialize(root)
122
- @root = root
123
- @created_files = { }
124
-
125
- # Keep track of changes to file counts and sizes made via Fuse - for #statfs
126
- @adj_nodes = 0
127
- @adj_size = 0
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)
136
- end
137
-
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
142
-
143
- def readdir(ctx,path,filler,offset,ffi)
144
-
145
- return wrap_context(ctx,__method__,path,filler,offset,ffi) if ctx
146
-
147
- #Always have "." and ".."
148
- filler.push(".",nil,0)
149
- filler.push("..",nil,0)
150
-
151
- files = @root.contents(path)
152
-
153
- files.each do | filename |
154
- filler.push(filename,nil,0)
155
- end
156
-
157
- end
158
-
159
- def getattr(ctx,path)
160
-
161
- return wrap_context(ctx,__method__,path) if ctx
162
-
163
- uid = Process.uid
164
- gid = Process.gid
165
-
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
169
-
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)
186
- end
187
-
188
- end #getattr
189
-
190
- def mkdir(ctx,path,mode)
191
-
192
- return wrap_context(ctx,__method__,path,mode) if ctx
193
-
194
- unless @root.can_mkdir?(path)
195
- raise Errno::EACCES.new(path)
196
- end
197
-
198
- @root.mkdir(path)
199
- @adj_nodes += 1
200
- end #mkdir
201
-
202
- def mknod(ctx,path,mode,major,minor)
203
-
204
- return wrap_context(ctx,__method__,path,mode,major,minor) if ctx
205
-
206
- unless ((RFuse::Stat::S_IFMT & mode) == RFuse::Stat::S_IFREG ) && @root.can_write?(path)
207
- raise Errno::EACCES.new(path)
208
- end
209
-
210
- now = Time.now
211
- stat = RFuse::Stat.file(mode,{ :uid => Process.uid, :gid => Process.gid, :atime => now, :mtime => now, :ctime => now })
212
-
213
- @created_files[path] = stat
214
- @adj_nodes += 1
215
- end #mknod
216
-
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)
220
-
221
- return wrap_context(ctx,__method__,path,offset,ffi) if ctx
222
-
223
- fh = ffi.fh
224
-
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
232
- end
233
- end
234
-
235
- #truncate a file outside of open files
236
- def truncate(ctx,path,offset)
237
- return wrap_context(ctx,__method__,path,offset) if ctx
238
-
239
- unless @root.can_write?(path)
240
- raise Errno::EACESS.new(path)
241
- end
242
-
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
251
- end
252
- @adj_size = @adj_size - current_size + (offset <= 0 ? 0 : offset)
253
- end #truncate
254
-
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)
261
-
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)
267
- end
268
-
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
276
-
277
- if @created_files.has_key?(path)
278
- fh.create
279
- else
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
286
- end
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
300
-
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 ""
312
- end
313
- rescue EOFError
314
- return ""
315
- end
316
-
317
- def write(ctx,path,buf,offset,ffi)
318
- return wrap_context(ctx,__method__,path,buf,offset,ffi) if ctx
319
- fh = ffi.fh
320
-
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)
330
- end
331
- end
332
-
333
- def fsync(ctx,path,datasync,ffi)
334
- return wrap_context(ctx,__method__,path,datasync,ffi) if ctx
335
- fh = ffi.fh
336
-
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)
345
- end
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()
504
- @root.mounted()
505
- end
506
-
507
- def unmounted()
508
- @root.unmounted()
509
- end
510
-
511
- def self.context(ctx,&block)
512
- begin
513
- Thread.current[:fusefs_reader_uid] = ctx.uid
514
- Thread.current[:fusefs_reader_gid] = ctx.gid
515
- yield
516
- ensure
517
- Thread.current[:fusefs_reader_uid] = nil
518
- Thread.current[:fusefs_reader_gid] = nil
519
- end
520
- end
521
-
522
- # @private - no doc
523
- def to_s
524
- "RFuseFS::#{@root}"
525
- end
526
-
527
- private
528
-
529
- def wrap_context(ctx,method,*args)
530
- self.class.context(ctx) { send(method,nil,*args) }
531
- end
532
-
533
- def size(path)
534
- @root.respond_to?(:size) ? @root.size(path) : @root.read_file(path).length
535
- end
536
-
537
- end #class Root
538
-
539
- end #class Fuse
540
- end #Module FuseFS