rfusefs 1.0.2.RC0 → 1.1.0.rc202009.34

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,46 +0,0 @@
1
- module FuseFS
2
-
3
- # A FuseFS over an existing directory
4
- class DirLink < FuseDir
5
-
6
- def initialize(dir)
7
- File.directory?(dir) or raise ArgumentError, "DirLink.initialize expects a valid directory!"
8
- @base = dir
9
- end
10
-
11
- def directory?(path)
12
- File.directory?(File.join(@base,path))
13
- end
14
-
15
- def file?(path)
16
- File.file?(File.join(@base,path))
17
- end
18
-
19
- def size(path)
20
- File.size(File.join(@base,path))
21
- end
22
-
23
- def contents(path)
24
- fn = File.join(@base,path)
25
- Dir.entries(fn).map { |file|
26
- file = file.sub(/^#{fn}\/?/,'')
27
- if ['..','.'].include?(file)
28
- nil
29
- else
30
- file
31
- end
32
- }.compact.sort
33
- end
34
-
35
- def read_file(path)
36
- fn = File.join(@base,path)
37
- if File.file?(fn)
38
- IO.read(fn)
39
- else
40
- 'No such file'
41
- end
42
- end
43
-
44
- end
45
-
46
- end
@@ -1,245 +0,0 @@
1
- module FuseFS
2
-
3
- # A full in-memory filesystem defined with hashes. It is writable to the
4
- # user that mounted it
5
- # may create and edit files within it, as well as the programmer
6
- # === Usage
7
- # root = Metadir.new()
8
- # root.mkdir("/hello")
9
- # root.write_to("/hello/world","Hello World!\n")
10
- # root.write_to("/hello/everybody","Hello Everyone!\n")
11
- #
12
- # FuseFS.start(mntpath,root)
13
- #
14
- # Because Metadir is fully recursive, you can mount your own or other defined
15
- # directory structures under it. For example, to mount a dictionary filesystem
16
- # (see samples/dictfs.rb), use:
17
- #
18
- # root.mkdir("/dict",DictFS.new())
19
- #
20
- class MetaDir
21
-
22
- DEFAULT_FS = FuseDir.new()
23
-
24
- def initialize()
25
- @subdirs = Hash.new(nil)
26
- @files = Hash.new(nil)
27
- end
28
-
29
- def split_path(path)
30
- DEFAULT_FS.split_path(path)
31
- end
32
-
33
- def scan_path
34
- DEFAULT_FS.scan_path(path)
35
- end
36
-
37
- def directory?(path)
38
- pathmethod(:directory?,path) do |filename|
39
- !filename || filename == "/" || @subdirs.has_key?(filename)
40
- end
41
- end
42
-
43
- def file?(path)
44
- pathmethod(:file?,path) do |filename|
45
- @files.has_key?(filename)
46
- end
47
- end
48
-
49
- #List directory contents
50
- def contents(path)
51
- pathmethod(:contents,path) do | filename |
52
- if !filename
53
- (@files.keys + @subdirs.keys).sort.uniq
54
- else
55
- @subdirs[filename].contents("/")
56
- end
57
- end
58
- end
59
-
60
- def read_file(path)
61
- pathmethod(:read_file,path) do |filename|
62
- @files[filename].to_s
63
- end
64
- end
65
-
66
- def size(path)
67
- pathmethod(:size,path) do | filename |
68
- return @files[filename].to_s.length
69
- end
70
- end
71
-
72
- #can_write only applies to files... see can_mkdir for directories...
73
- def can_write?(path)
74
- pathmethod(:can_write?,path) do |filename|
75
- return mount_user?
76
- end
77
- end
78
-
79
- def write_to(path,contents)
80
- pathmethod(:write_to,path,contents) do |filename, filecontents |
81
- @files[filename] = filecontents
82
- end
83
- end
84
-
85
- # Delete a file
86
- def can_delete?(path)
87
- pathmethod(:can_delete?,path) do |filename|
88
- return mount_user?
89
- end
90
- end
91
-
92
- def delete(path)
93
- pathmethod(:delete,path) do |filename|
94
- @files.delete(filename)
95
- end
96
- end
97
-
98
- #mkdir - does not make intermediate dirs!
99
- def can_mkdir?(path)
100
- pathmethod(:can_mkdir?,path) do |dirname|
101
- return mount_user?
102
- end
103
- end
104
-
105
- def mkdir(path,dir=nil)
106
- pathmethod(:mkdir,path,dir) do | dirname,dirobj |
107
- dirobj ||= MetaDir.new
108
- @subdirs[dirname] = dirobj
109
- end
110
- end
111
-
112
- # Delete an existing directory make sure it is not empty
113
- def can_rmdir?(path)
114
- pathmethod(:can_rmdir?,path) do |dirname|
115
- return mount_user? && @subdirs.has_key?(dirname) && @subdirs[dirname].contents("/").empty?
116
- end
117
- end
118
-
119
- def rmdir(path)
120
- pathmethod(:rmdir,path) do |dirname|
121
- @subdirs.delete(dirname)
122
- end
123
- end
124
-
125
- def rename(from_path,to_path,to_fusefs = self)
126
-
127
- from_base,from_rest = split_path(from_path)
128
-
129
- case
130
- when !from_base
131
- # Shouldn't ever happen.
132
- raise Errno::EACCES.new("Can't move root")
133
- when !from_rest
134
- # So now we have a file or directory to move
135
- if @files.has_key?(from_base)
136
- return false unless can_delete?(from_base) && to_fusefs.can_write?(to_path)
137
- to_fusefs.write_to(to_path,@files[from_base])
138
- @files.delete(from_base)
139
- elsif @subdirs.has_key?(from_base)
140
- # we don't check can_rmdir? because that would prevent us
141
- # moving non empty directories
142
- return false unless mount_user? && to_fusefs.can_mkdir?(to_path)
143
- begin
144
- to_fusefs.mkdir(to_path,@subdirs[from_base])
145
- @subdirs.delete(from_base)
146
- rescue ArgumentError
147
- # to_rest does not support mkdir with an arbitrary object
148
- return false
149
- end
150
- else
151
- #We shouldn't get this either
152
- return false
153
- end
154
- when @subdirs.has_key?(from_base)
155
- begin
156
- if to_fusefs != self
157
- #just keep recursing..
158
- return @subdirs[from_base].rename(from_rest,to_path,to_fusefs)
159
- else
160
- to_base,to_rest = split_path(to_path)
161
- if from_base == to_base
162
- #mv within a subdir, just pass it on
163
- return @subdirs[from_base].rename(from_rest,to_rest)
164
- else
165
- #OK, this is the tricky part, we want to move something further down
166
- #our tree into something in another part of the tree.
167
- #from this point on we keep a reference to the fusefs that owns
168
- #to_path (ie us) and pass it down, but only if the eventual path
169
- #is writable anyway!
170
- if (file?(to_path))
171
- return false unless can_write?(to_path)
172
- else
173
- return false unless can_mkdir?(to_path)
174
- end
175
-
176
- return @subdirs[from_base].rename(from_rest,to_path,self)
177
- end
178
- end
179
- rescue NoMethodError
180
- #sub dir doesn't support rename
181
- return false
182
- rescue ArgumentError
183
- #sub dir doesn't support rename with additional to_fusefs argument
184
- return false
185
- end
186
- else
187
- return false
188
- end
189
- end
190
-
191
- default_methods = FuseDir.public_instance_methods.select { |m|
192
- ![:mounted,:unmounted].include?(m) &&
193
- !self.public_method_defined?(m) && FuseDir.instance_method(m).owner == FuseDir
194
- }
195
-
196
- default_methods.each do |m|
197
- define_method(m) do |*args|
198
- pathmethod(m,*args) { |*args| DEFAULT_FS.send(m,*args) }
199
- end
200
- end
201
-
202
- private
203
- # is the accessing user the same as the user that mounted our FS?, used for
204
- # all write activity
205
- def mount_user?
206
- return Process.uid == FuseFS.reader_uid
207
- end
208
-
209
- #All our FuseFS methods follow the same pattern...
210
- def pathmethod(method, path,*args)
211
- base,rest = split_path(path)
212
-
213
- case
214
- when ! base
215
- #request for the root of our fs
216
- yield(nil,*args)
217
- when ! rest
218
- #base is the filename, no more directories to traverse
219
- yield(base,*args)
220
- when @subdirs.has_key?(base)
221
- #base is a subdirectory, pass it on if we can
222
- begin
223
- @subdirs[base].send(method,rest,*args)
224
- rescue NoMethodError
225
- #Oh well
226
- return DEFAULT_FS.send(method,rest,*args)
227
- rescue ArgumentError
228
- #can_mkdir,mkdir
229
- if args.pop.nil?
230
- #possibly a default arg, try sending again with one fewer arg
231
- @subdirs[base].send(method,rest,*args)
232
- else
233
- #definitely not a default arg, reraise
234
- Kernel.raise
235
- end
236
- end
237
- else
238
- #return the default response
239
- return DEFAULT_FS.send(method,path,*args)
240
- end
241
- end
242
-
243
-
244
- end
245
- end
@@ -1,335 +0,0 @@
1
-
2
- module FuseFS
3
-
4
- # A FuseFS that maps files from their original location into a new path
5
- # eg tagged audio files can be mapped by title etc...
6
- #
7
- class PathMapperFS < FuseDir
8
-
9
- # Represents a mappted file or directory
10
- class MNode
11
-
12
- # @return [Hash<String,MNode>] list of files in a directory, nil for file nodes
13
- attr_reader :files
14
-
15
- # @return [MNode] parent directory
16
- attr_reader :parent
17
-
18
- # @return [Hash] metadata for this node
19
- attr_reader :options
20
-
21
- # @return [String] path to backing file, or nil for directory nodes
22
- attr_reader :real_path
23
-
24
- # @!visibility private
25
- def initialize(parent_dir)
26
- @parent = parent_dir
27
- @files = {}
28
- @options = {}
29
- end
30
-
31
- # @!visibility private
32
- def init_file(real_path,options)
33
- @options.merge!(options)
34
- @real_path = real_path
35
- @files = nil
36
- self
37
- end
38
-
39
- def init_dir(options)
40
- @options.merge!(options)
41
- self
42
- end
43
-
44
- # @return [Boolean] true if node represents a file, otherwise false
45
- def file?
46
- real_path && true
47
- end
48
-
49
- # @return [Boolean] true if node represents a directory, otherwise false
50
- def directory?
51
- files && true
52
- end
53
-
54
- # @return [Boolean] true if node is the root directory
55
- def root?
56
- @parent.nil?
57
- end
58
-
59
- # Compatibility and convenience method
60
- # @param [:pm_real_path,String,Symbol] key
61
- # @return [String] {#real_path} if key == :pm_real_path
62
- # @return [MNode] the node representing the file named key
63
- # @return [Object] shortcut for {#options}[key]
64
- def[](key)
65
- case key
66
- when :pm_real_path
67
- real_path
68
- when String
69
- files[key]
70
- else
71
- options[key]
72
- end
73
- end
74
-
75
- # Convenience method to set metadata into {#options}
76
- def[]=(key,value)
77
- options[key]=value
78
- end
79
- end
80
-
81
- # Convert FuseFS raw_mode strings to IO open mode strings
82
- def self.open_mode(raw_mode)
83
- case raw_mode
84
- when "r"
85
- "r"
86
- when "ra"
87
- "r" #not really sensible..
88
- when "rw"
89
- "r+"
90
- when "rwa"
91
- "a+"
92
- when "w"
93
- "w"
94
- when "wa"
95
- "a"
96
- end
97
- end
98
-
99
- # should raw file access should be used - useful for binary files
100
- # @return [Boolean]
101
- # default is false
102
- attr_accessor :use_raw_file_access
103
-
104
- # should filesystem support writing through to the real files
105
- # @return [Boolean]
106
- # default is false
107
- attr_accessor :allow_write
108
-
109
- # Creates a new Path Mapper filesystem over an existing directory
110
- # @param [String] dir
111
- # @param [Hash] options
112
- # @yieldparam [String] file path to map
113
- # @yieldreturn [String]
114
- # @see #initialize
115
- # @see #map_directory
116
- def PathMapperFS.create(dir,options={ },&block)
117
- pm_fs = PathMapperFS.new(options)
118
- pm_fs.map_directory(dir,&block)
119
- return pm_fs
120
- end
121
-
122
- # Create a new Path Mapper filesystem
123
- # @param [Hash] options
124
- # @option options [Boolean] :use_raw_file_access
125
- # @option options [Boolean] :allow_write
126
- def initialize(options = { })
127
- @root = MNode.new(nil)
128
- @use_raw_file_access = options[:use_raw_file_access]
129
- @allow_write = options[:allow_write]
130
- end
131
-
132
- # Recursively find all files and map according to the given block
133
- # @param [String...] dirs directories to list
134
- # @yieldparam [String] file path to map
135
- # @yieldreturn [String] the mapped path
136
- # @yieldreturn nil to skip mapping this file
137
- def map_directory(*dirs)
138
- require 'find'
139
- Find.find(*dirs) do |file|
140
- new_path = yield file
141
- map_file(file,new_path) if new_path
142
- end
143
- end
144
- alias :mapDirectory :map_directory
145
-
146
-
147
- # Add (or replace) a mapped file
148
- #
149
- # @param [String] real_path pointing at the real file location
150
- # @param [String] new_path the mapped path
151
- # @param [Hash<Symbol,Object>] options metadata for this path
152
- # @option options [Hash<String,String>] :xattr hash to be used as extended attributes
153
- # @return [MNode]
154
- # a node representing the mapped path. See {#node}
155
- def map_file(real_path,new_path,options = {})
156
- make_node(new_path).init_file(real_path,options)
157
- end
158
- alias :mapFile :map_file
159
-
160
- # Retrieve in memory node for a mapped path
161
- #
162
- # @param [String] path
163
- # @return [MNode] in memory node at path
164
- # @return nil if path does not exist in the filesystem
165
- def node(path)
166
- path_components = scan_path(path)
167
-
168
- #not actually injecting anything here, we're just following the hash of hashes...
169
- path_components.inject(@root) { |dir,file|
170
- break unless dir.files[file]
171
- dir.files[file]
172
- }
173
- end
174
-
175
- # Takes a mapped file name and returns the original real_path
176
- def unmap(path)
177
- node = node(path)
178
- (node && node.file?) ? node.real_path : nil
179
- end
180
-
181
- # Deletes files and directories.
182
- # Yields each {#node} in the filesystem and deletes it if the block returns true
183
- #
184
- # Useful if your filesystem is periodically remapping the entire contents and you need
185
- # to delete entries that have not been touched in the latest scan
186
- #
187
- # @yieldparam [Hash] filesystem node
188
- # @yieldreturn [true,false] should this node be deleted
189
- def cleanup(&block)
190
- recursive_cleanup(@root,&block)
191
- end
192
-
193
-
194
- # @!visibility private
195
- def directory?(path)
196
- possible_dir = node(path)
197
- possible_dir && possible_dir.directory?
198
- end
199
-
200
- # @!visibility private
201
- def contents(path)
202
- node(path).files.keys
203
- end
204
-
205
- # @!visibility private
206
- def file?(path)
207
- filename = unmap(path)
208
- filename && File.file?(filename)
209
- end
210
-
211
- # @!visibility private
212
- # only called if option :raw_reads is not set
213
- def read_file(path)
214
- IO.read(unmap(path))
215
- end
216
-
217
- # @!visibility private
218
- # We can only write to existing files
219
- # because otherwise we don't have anything to back it
220
- def can_write?(path)
221
- @allow_write && file?(path)
222
- end
223
-
224
- # Note we don't impleemnt can_mkdir? so this can
225
- # only be called by code. Really only useful to
226
- # create empty directories
227
- def mkdir(path,options = {})
228
- make_node(path).init_dir(options)
229
- end
230
-
231
- # @!visibility private
232
- def write_to(path,contents)
233
- File.open(unmap(path),"w") do |f|
234
- f.print(contents)
235
- end
236
- end
237
-
238
- # @!visibility private
239
- def size(path)
240
- File.size(unmap(path))
241
- end
242
-
243
- # @!visibility private
244
- def times(path)
245
- realpath = unmap(path)
246
- if (realpath)
247
- stat = File.stat(realpath)
248
- return [ stat.atime, stat.mtime, stat.ctime ]
249
- else
250
- # We're a directory
251
- return [0,0,0]
252
- end
253
- end
254
-
255
- # @!visibility private
256
- def xattr(path)
257
- result = node(path).options[:xattr] || {}
258
- end
259
-
260
- # @!visibility private
261
- # Will create, store and return a File object for the underlying file
262
- # for subsequent use with the raw_read/raw_close methods
263
- # expects file? to return true before this method is called
264
- def raw_open(path,mode,rfusefs = nil)
265
-
266
- return false unless @use_raw_file_access
267
-
268
- return false if mode.include?("w") && (!@allow_write)
269
-
270
- @openfiles ||= Hash.new() unless rfusefs
271
-
272
- real_path = unmap(path)
273
-
274
- unless real_path
275
- if rfusefs
276
- raise Errno::ENOENT.new(path)
277
- else
278
- #fusefs will go on to call file?
279
- return false
280
- end
281
- end
282
-
283
- file = File.new(real_path,PathMapperFS.open_mode(mode))
284
-
285
- @openfiles[path] = file unless rfusefs
286
-
287
- return file
288
- end
289
-
290
- # @!visibility private
291
- def raw_read(path,off,sz,file=nil)
292
- file = @openfiles[path] unless file
293
- file.sysseek(off)
294
- file.sysread(sz)
295
- end
296
-
297
- # @!visibility private
298
- def raw_write(path,offset,sz,buf,file=nil)
299
- file = @openfiles[path] unless file
300
- file.sysseek(offset)
301
- file.syswrite(buf[0,sz])
302
- end
303
-
304
- # @!visibility private
305
- def raw_close(path,file=nil)
306
- unless file
307
- file = @openfiles.delete(path)
308
- end
309
- file.close if file
310
- end
311
-
312
- private
313
-
314
- def make_node(path)
315
- #split path into components
316
- components = path.to_s.scan(/[^\/]+/)
317
- components.inject(@root) { |parent_dir, file|
318
- parent_dir.files[file] ||= MNode.new(parent_dir)
319
- }
320
- end
321
-
322
- def recursive_cleanup(dir_node,&block)
323
- dir_node.files.delete_if do |path,child|
324
- if child.file?
325
- yield child
326
- else
327
- recursive_cleanup(child,&block)
328
- child.files.size == 0
329
- end
330
- end
331
- end
332
- end
333
-
334
- end
335
-