rfusefs 1.0.0 → 1.0.1.RC0
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.
- data/.gitignore +1 -1
- data/.travis.yml +8 -0
- data/README.rdoc +13 -21
- data/Rakefile +1 -0
- data/lib/fuse/fusedir.rb +15 -0
- data/lib/fuse/rfusefs-fuse.rb +26 -8
- data/lib/fusefs/metadir.rb +2 -1
- data/lib/fusefs/pathmapper.rb +185 -56
- data/lib/fusefs/sqlitemapper.rb +125 -0
- data/lib/rfusefs/version.rb +1 -1
- data/lib/rfusefs.rb +55 -1
- data/rfusefs.gemspec +30 -0
- data/spec/metadir_spec.rb +24 -24
- data/spec/mount_unmount_spec.rb +21 -0
- data/spec/pathmapper_spec.rb +143 -53
- data/spec/rfusefs_spec.rb +23 -1
- data/spec/sample_spec.rb +9 -9
- data/spec/spec_helper.rb +0 -5
- data/spec/sqlitemapper_spec.rb +135 -0
- metadata +49 -9
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/README.rdoc
CHANGED
@@ -16,22 +16,7 @@ RFuseFS is api compatible with FuseFS (0.7.0)
|
|
16
16
|
FuseFS provides a layer of abstraction to a programmer who wants to create a
|
17
17
|
virtual filesystem via FUSE.
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
1. FuseFS, which is defined in 'rfusefs.rb'
|
22
|
-
2. An object that defines a virtual directory by subclassing {FuseFS::FuseDir}
|
23
|
-
|
24
|
-
To write a FuseFS program, you must:
|
25
|
-
|
26
|
-
* Define and create a Directory object that responds to the methods required
|
27
|
-
by FuseFS for its desired use.
|
28
|
-
|
29
|
-
* Call
|
30
|
-
|
31
|
-
FuseFS.start <virtualdir> <mountpoint> [mount_options]
|
32
|
-
|
33
|
-
where <virtualdir> with an object defining your virtual directory.
|
34
|
-
and <mountpoint> is a real directory on your filesystem
|
19
|
+
First define a virtual directory by subclassing {FuseFS::FuseDir}
|
35
20
|
|
36
21
|
See samples under /samples and also the following starter classes
|
37
22
|
|
@@ -39,13 +24,19 @@ See samples under /samples and also the following starter classes
|
|
39
24
|
* {FuseFS::MetaDir}
|
40
25
|
* {FuseFS::DirLink}
|
41
26
|
* {FuseFS::PathMapperFS}
|
27
|
+
* {FuseFS::SqliteMapperFS}
|
42
28
|
|
43
|
-
|
29
|
+
Then start your filesystem with
|
30
|
+
|
31
|
+
* {FuseFS.start}
|
32
|
+
* {FuseFS.main}
|
33
|
+
|
34
|
+
Finally to use the filesystem open up your favourite file browser/terminal and
|
44
35
|
explore the contents under <mountpoint>
|
45
36
|
|
46
37
|
Happy Filesystem Hacking!
|
47
38
|
|
48
|
-
=== the hello world filesystem in
|
39
|
+
=== the hello world filesystem in 16 LOC
|
49
40
|
|
50
41
|
require 'rfusefs'
|
51
42
|
|
@@ -59,12 +50,13 @@ Happy Filesystem Hacking!
|
|
59
50
|
def read_file(path)
|
60
51
|
"Hello, World!\n"
|
61
52
|
end
|
53
|
+
def size(path)
|
54
|
+
read_file(path).size
|
55
|
+
end
|
62
56
|
end
|
63
57
|
|
64
|
-
hellodir = HelloDir.new
|
65
|
-
|
66
58
|
# Usage: #{$0} mountpoint [mount_options]
|
67
|
-
FuseFS.
|
59
|
+
FuseFS.main() { |options| HelloDir.new }
|
68
60
|
|
69
61
|
== REQUIREMENTS:
|
70
62
|
|
data/Rakefile
CHANGED
data/lib/fuse/fusedir.rb
CHANGED
@@ -196,6 +196,21 @@ module FuseFS
|
|
196
196
|
# @abstract FuseFS api
|
197
197
|
def raw_close(path,raw=nil);end
|
198
198
|
|
199
|
+
# RFuseFS extension.
|
200
|
+
# Extended attributes. These will be set/retrieved/removed directly
|
201
|
+
# @param [String] path
|
202
|
+
# @return [Hash] extended attributes for this path
|
203
|
+
# @abstract FuseFS api
|
204
|
+
def xattr(path); return {}; end
|
205
|
+
|
206
|
+
# RFuseFS extension.
|
207
|
+
# Called when the filesystem is mounted
|
208
|
+
def mounted();end
|
209
|
+
|
210
|
+
# RFuseFS extension.
|
211
|
+
# Called when the filesystem is unmounted
|
212
|
+
def unmounted();end
|
213
|
+
|
199
214
|
end
|
200
215
|
|
201
216
|
DEFAULT_FS = FuseDir.new()
|
data/lib/fuse/rfusefs-fuse.rb
CHANGED
@@ -371,17 +371,27 @@ module FuseFS
|
|
371
371
|
#def link(path,as)
|
372
372
|
#end
|
373
373
|
|
374
|
-
|
375
|
-
|
374
|
+
def setxattr(ctx,path,name,value)
|
375
|
+
return wrap_context(ctx,__method__,path,name,value) if ctx
|
376
|
+
@root.xattr(path)[name]=value
|
377
|
+
end
|
376
378
|
|
377
|
-
|
378
|
-
|
379
|
+
def getxattr(ctx,path,name)
|
380
|
+
return wrap_context(ctx,__method__,path,name) if ctx
|
381
|
+
result = @root.xattr(path)[name]
|
382
|
+
raise Errno::ENODATA.new("No attribute #{name}") unless result
|
383
|
+
result
|
384
|
+
end
|
379
385
|
|
380
|
-
|
381
|
-
|
386
|
+
def listxattr(ctx,path)
|
387
|
+
return wrap_context(ctx,__method__,path) if ctx
|
388
|
+
@root.xattr(path).keys
|
389
|
+
end
|
382
390
|
|
383
|
-
|
384
|
-
|
391
|
+
def removexattr(ctx,path,name)
|
392
|
+
return wrap_context(ctx,__method__,path,name) if ctx
|
393
|
+
@root.xattr(path).delete(name)
|
394
|
+
end
|
385
395
|
|
386
396
|
#def opendir(path,ffi)
|
387
397
|
#end
|
@@ -396,6 +406,14 @@ module FuseFS
|
|
396
406
|
#def statfs(path)
|
397
407
|
#end
|
398
408
|
|
409
|
+
def mounted()
|
410
|
+
@root.mounted()
|
411
|
+
end
|
412
|
+
|
413
|
+
def unmounted()
|
414
|
+
@root.unmounted()
|
415
|
+
end
|
416
|
+
|
399
417
|
def self.context(ctx,&block)
|
400
418
|
begin
|
401
419
|
Thread.current[:fusefs_reader_uid] = ctx.uid
|
data/lib/fusefs/metadir.rb
CHANGED
@@ -189,7 +189,8 @@ module FuseFS
|
|
189
189
|
end
|
190
190
|
|
191
191
|
default_methods = FuseDir.public_instance_methods.select { |m|
|
192
|
-
!
|
192
|
+
![:mounted,:unmounted].include?(m) &&
|
193
|
+
!self.public_method_defined?(m) && FuseDir.instance_method(m).owner == FuseDir
|
193
194
|
}
|
194
195
|
|
195
196
|
default_methods.each do |m|
|
data/lib/fusefs/pathmapper.rb
CHANGED
@@ -1,10 +1,78 @@
|
|
1
1
|
|
2
2
|
module FuseFS
|
3
3
|
|
4
|
-
# A FuseFS that maps files from
|
4
|
+
# A FuseFS that maps files from their original location into a new path
|
5
5
|
# eg tagged audio files can be mapped by title etc...
|
6
|
+
#
|
6
7
|
class PathMapperFS < FuseDir
|
7
|
-
|
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 = options
|
34
|
+
@real_path = real_path
|
35
|
+
@files = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Boolean] true if node represents a file, otherwise false
|
39
|
+
def file?
|
40
|
+
real_path && true
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [Boolean] true if node represents a directory, otherwise false
|
44
|
+
def directory?
|
45
|
+
files && true
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [Boolean] true if node is the root directory
|
49
|
+
def root?
|
50
|
+
@parent.nil?
|
51
|
+
end
|
52
|
+
|
53
|
+
# Compatibility and convenience method
|
54
|
+
# @param [:pm_real_path,String,Symbol] key
|
55
|
+
# @return [String] {#real_path} if key == :pm_real_path
|
56
|
+
# @return [MNode] the node representing the file named key
|
57
|
+
# @return [Object] shortcut for {#options}[key]
|
58
|
+
def[](key)
|
59
|
+
case key
|
60
|
+
when :pm_real_path
|
61
|
+
real_path
|
62
|
+
when String
|
63
|
+
files[key]
|
64
|
+
else
|
65
|
+
options[key]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Convenience method to set metadata into {#options}
|
70
|
+
def[]=(key,value)
|
71
|
+
options[key]=value
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Convert FuseFS raw_mode strings to IO open mode strings
|
8
76
|
def self.open_mode(raw_mode)
|
9
77
|
case raw_mode
|
10
78
|
when "r"
|
@@ -12,107 +80,163 @@ module FuseFS
|
|
12
80
|
when "ra"
|
13
81
|
"r" #not really sensible..
|
14
82
|
when "rw"
|
15
|
-
"
|
83
|
+
"r+"
|
16
84
|
when "rwa"
|
17
85
|
"a+"
|
18
|
-
when
|
86
|
+
when "w"
|
19
87
|
"w"
|
20
88
|
when "wa"
|
21
89
|
"a"
|
22
90
|
end
|
23
91
|
end
|
24
|
-
|
25
|
-
#
|
26
|
-
#
|
92
|
+
|
93
|
+
# should raw file access should be used - useful for binary files
|
94
|
+
# @return [Boolean]
|
95
|
+
# default is false
|
96
|
+
attr_accessor :use_raw_file_access
|
97
|
+
|
98
|
+
# should filesystem support writing through to the real files
|
99
|
+
# @return [Boolean]
|
100
|
+
# default is false
|
101
|
+
attr_accessor :allow_write
|
102
|
+
|
103
|
+
# Creates a new Path Mapper filesystem over an existing directory
|
104
|
+
# @param [String] dir
|
105
|
+
# @param [Hash] options
|
106
|
+
# @yieldparam [String] file path to map
|
107
|
+
# @yieldreturn [String]
|
108
|
+
# @see #initialize
|
109
|
+
# @see #map_directory
|
27
110
|
def PathMapperFS.create(dir,options={ },&block)
|
28
111
|
pm_fs = PathMapperFS.new(options)
|
29
|
-
pm_fs.
|
30
|
-
block.call(file)
|
31
|
-
end
|
112
|
+
pm_fs.map_directory(dir,&block)
|
32
113
|
return pm_fs
|
33
114
|
end
|
34
115
|
|
116
|
+
# Create a new Path Mapper filesystem
|
117
|
+
# @param [Hash] options
|
118
|
+
# @option options [Boolean] :use_raw_file_access
|
119
|
+
# @option options [Boolean] :allow_write
|
35
120
|
def initialize(options = { })
|
36
|
-
@root =
|
121
|
+
@root = MNode.new(nil)
|
37
122
|
@use_raw_file_access = options[:use_raw_file_access]
|
38
123
|
@allow_write = options[:allow_write]
|
39
124
|
end
|
125
|
+
|
126
|
+
# Recursively find all files and map according to the given block
|
127
|
+
# @param [String...] dirs directories to list
|
128
|
+
# @yieldparam [String] file path to map
|
129
|
+
# @yieldreturn [String] the mapped path
|
130
|
+
# @yieldreturn nil to skip mapping this file
|
131
|
+
def map_directory(*dirs)
|
132
|
+
require 'find'
|
133
|
+
Find.find(*dirs) do |file|
|
134
|
+
new_path = yield file
|
135
|
+
map_file(file,new_path) if new_path
|
136
|
+
end
|
137
|
+
end
|
138
|
+
alias :mapDirectory :map_directory
|
40
139
|
|
41
|
-
|
140
|
+
|
141
|
+
# Add (or replace) a mapped file
|
42
142
|
#
|
43
|
-
#
|
44
|
-
|
143
|
+
# @param [String] real_path pointing at the real file location
|
144
|
+
# @param [String] new_path the mapped path
|
145
|
+
# @param [Hash<Symbol,Object>] options metadata for this path
|
146
|
+
# @option options [Hash<String,String>] :xattr hash to be used as extended attributes
|
147
|
+
# @return [MNode]
|
148
|
+
# a node representing the mapped path. See {#node}
|
149
|
+
def map_file(real_path,new_path,options = {})
|
45
150
|
#split path into components
|
46
|
-
components = new_path.scan(/[^\/]+/)
|
151
|
+
components = new_path.to_s.scan(/[^\/]+/)
|
47
152
|
|
48
153
|
#create a hash of hashes to represent our directory structure
|
49
|
-
new_file = components.inject(@root) { |
|
50
|
-
|
154
|
+
new_file = components.inject(@root) { |parent_dir, file|
|
155
|
+
parent_dir.files[file] ||= MNode.new(parent_dir)
|
51
156
|
}
|
52
|
-
new_file
|
157
|
+
new_file.init_file(real_path,options)
|
158
|
+
|
53
159
|
return new_file
|
54
160
|
end
|
161
|
+
alias :mapFile :map_file
|
162
|
+
|
163
|
+
# Retrieve in memory node for a mapped path
|
164
|
+
#
|
165
|
+
# @param [String] path
|
166
|
+
# @return [MNode] in memory node at path
|
167
|
+
# @return nil if path does not exist in the filesystem
|
168
|
+
def node(path)
|
169
|
+
path_components = scan_path(path)
|
55
170
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
mapFile(file,new_path) if new_path
|
62
|
-
end
|
171
|
+
#not actually injecting anything here, we're just following the hash of hashes...
|
172
|
+
path_components.inject(@root) { |dir,file|
|
173
|
+
break unless dir.files[file]
|
174
|
+
dir.files[file]
|
175
|
+
}
|
63
176
|
end
|
64
177
|
|
65
178
|
# Takes a mapped file name and returns the original real_path
|
66
179
|
def unmap(path)
|
67
|
-
|
68
|
-
|
180
|
+
node = node(path)
|
181
|
+
(node && node.file?) ? node.real_path : nil
|
69
182
|
end
|
183
|
+
|
184
|
+
# Deletes files and directories.
|
185
|
+
# Yields each {#node} in the filesystem and deletes it if the block returns true
|
186
|
+
#
|
187
|
+
# Useful if your filesystem is periodically remapping the entire contents and you need
|
188
|
+
# to delete entries that have not been touched in the latest scan
|
189
|
+
#
|
190
|
+
# @yieldparam [Hash] filesystem node
|
191
|
+
# @yieldreturn [true,false] should this node be deleted
|
192
|
+
def cleanup(&block)
|
193
|
+
recursive_cleanup(@root,&block)
|
194
|
+
end
|
195
|
+
|
70
196
|
|
71
|
-
#
|
72
|
-
# See FuseFS API.txt
|
197
|
+
# @!visibility private
|
73
198
|
def directory?(path)
|
74
199
|
possible_dir = node(path)
|
75
|
-
possible_dir &&
|
200
|
+
possible_dir && possible_dir.directory?
|
76
201
|
end
|
77
202
|
|
78
|
-
#
|
79
|
-
# expects to be called only if directory? returns true
|
203
|
+
# @!visibility private
|
80
204
|
def contents(path)
|
81
|
-
node(path).keys
|
205
|
+
node(path).files.keys
|
82
206
|
end
|
83
207
|
|
84
|
-
#
|
208
|
+
# @!visibility private
|
85
209
|
def file?(path)
|
86
210
|
filename = unmap(path)
|
87
211
|
filename && File.file?(filename)
|
88
212
|
end
|
89
213
|
|
90
|
-
#
|
214
|
+
# @!visibility private
|
91
215
|
# only called if option :raw_reads is not set
|
92
216
|
def read_file(path)
|
93
217
|
IO.read(unmap(path))
|
94
218
|
end
|
95
219
|
|
220
|
+
# @!visibility private
|
96
221
|
# We can only write to existing files
|
97
222
|
# because otherwise we don't have anything to back it
|
98
223
|
def can_write?(path)
|
99
224
|
@allow_write && file?(path)
|
100
225
|
end
|
101
226
|
|
102
|
-
#
|
103
|
-
# and we don't open the file for writing
|
227
|
+
# @!visibility private
|
104
228
|
def write_to(path,contents)
|
105
|
-
File.open(path) do |f|
|
229
|
+
File.open(unmap(path),"w") do |f|
|
106
230
|
f.print(contents)
|
107
231
|
end
|
108
232
|
end
|
109
233
|
|
110
|
-
#
|
234
|
+
# @!visibility private
|
111
235
|
def size(path)
|
112
236
|
File.size(unmap(path))
|
113
237
|
end
|
114
238
|
|
115
|
-
#
|
239
|
+
# @!visibility private
|
116
240
|
def times(path)
|
117
241
|
realpath = unmap(path)
|
118
242
|
if (realpath)
|
@@ -124,7 +248,12 @@ module FuseFS
|
|
124
248
|
end
|
125
249
|
end
|
126
250
|
|
127
|
-
#
|
251
|
+
# @!visibility private
|
252
|
+
def xattr(path)
|
253
|
+
result = node(path).options[:xattr] || {}
|
254
|
+
end
|
255
|
+
|
256
|
+
# @!visibility private
|
128
257
|
# Will create, store and return a File object for the underlying file
|
129
258
|
# for subsequent use with the raw_read/raw_close methods
|
130
259
|
# expects file? to return true before this method is called
|
@@ -132,7 +261,7 @@ module FuseFS
|
|
132
261
|
|
133
262
|
return false unless @use_raw_file_access
|
134
263
|
|
135
|
-
return false if mode.include?("w") && (!@
|
264
|
+
return false if mode.include?("w") && (!@allow_write)
|
136
265
|
|
137
266
|
@openfiles ||= Hash.new() unless rfusefs
|
138
267
|
|
@@ -154,21 +283,21 @@ module FuseFS
|
|
154
283
|
return file
|
155
284
|
end
|
156
285
|
|
157
|
-
#
|
286
|
+
# @!visibility private
|
158
287
|
def raw_read(path,off,sz,file=nil)
|
159
288
|
file = @openfiles[path] unless file
|
160
289
|
file.sysseek(off)
|
161
290
|
file.sysread(sz)
|
162
291
|
end
|
163
292
|
|
164
|
-
#
|
293
|
+
# @!visibility private
|
165
294
|
def raw_write(path,offset,sz,buf,file=nil)
|
166
295
|
file = @openfiles[path] unless file
|
167
|
-
file.sysseek(
|
296
|
+
file.sysseek(offset)
|
168
297
|
file.syswrite(buf[0,sz])
|
169
298
|
end
|
170
299
|
|
171
|
-
#
|
300
|
+
# @!visibility private
|
172
301
|
def raw_close(path,file=nil)
|
173
302
|
unless file
|
174
303
|
file = @openfiles.delete(path)
|
@@ -177,16 +306,16 @@ module FuseFS
|
|
177
306
|
end
|
178
307
|
|
179
308
|
private
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
309
|
+
|
310
|
+
def recursive_cleanup(dir_node,&block)
|
311
|
+
dir_node.files.delete_if do |path,child|
|
312
|
+
if child.file?
|
313
|
+
yield child
|
314
|
+
else
|
315
|
+
recursive_cleanup(child,&block)
|
316
|
+
child.files.size == 0
|
317
|
+
end
|
318
|
+
end
|
190
319
|
end
|
191
320
|
end
|
192
321
|
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'fusefs/pathmapper'
|
2
|
+
require 'sqlite3'
|
3
|
+
require 'rb-inotify'
|
4
|
+
require 'thread'
|
5
|
+
|
6
|
+
module FuseFS
|
7
|
+
|
8
|
+
class SqliteMapperFS < PathMapperFS
|
9
|
+
|
10
|
+
# The database connection
|
11
|
+
attr_reader :db
|
12
|
+
|
13
|
+
# Maintains a count of the number of times through the scan loop
|
14
|
+
attr_reader :scan_id
|
15
|
+
|
16
|
+
#
|
17
|
+
#
|
18
|
+
# @param [String] db_path Path to Sqlite database
|
19
|
+
# @param [String] sql query
|
20
|
+
# @param [Hash] options see {PathMapperFS#initialize}
|
21
|
+
# @yieldparam [Row] row to map
|
22
|
+
# @yieldreturn [String,String,Hash<Symbol,Object>] newpath, realpath, options
|
23
|
+
# * newpath - the mapped path
|
24
|
+
# * realpath - path to the real file
|
25
|
+
# * options - additional information to store with this path
|
26
|
+
def initialize(db_path,sql,options = { },&row_mapper)
|
27
|
+
@db_path = db_path.to_s
|
28
|
+
@sql = sql.to_s
|
29
|
+
define_singleton_method(:map_row,row_mapper) if block_given?
|
30
|
+
super(options)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Maps a row into a new filepath
|
34
|
+
#
|
35
|
+
# @param [Hash] row sqlite result hash for a row
|
36
|
+
# @return [String,String,Hash<Symbol,Object>] newpath, realpath, options
|
37
|
+
# * newpath - the mapped path
|
38
|
+
# * realpath - path to the real file
|
39
|
+
# * options - additional information to store with this path
|
40
|
+
# @abstract
|
41
|
+
def map_row(row)
|
42
|
+
raise NotImplementedError, "abstract method #{__method__} not implemented"
|
43
|
+
end
|
44
|
+
|
45
|
+
# FuseFS callback when the filesystem is mounted
|
46
|
+
# performs the initial scan and starts watching the database for changes
|
47
|
+
# @api FuseFS
|
48
|
+
def mounted()
|
49
|
+
@mutex = Mutex.new
|
50
|
+
@cv = ConditionVariable.new
|
51
|
+
@mounted = true
|
52
|
+
|
53
|
+
notifier = start_notifier
|
54
|
+
|
55
|
+
|
56
|
+
@scan_thread = Thread.new() do
|
57
|
+
scan_loop(notifier)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# FuseFS callback when filesystem is unmounted
|
62
|
+
#
|
63
|
+
# Stops the database watching threads
|
64
|
+
# @api FuseFS
|
65
|
+
def unmounted()
|
66
|
+
@mounted = false
|
67
|
+
@mutex.synchronize { @cv.signal }
|
68
|
+
@scan_thread.join
|
69
|
+
end
|
70
|
+
|
71
|
+
# Executes the sql query and passes each row to map_row (or the block passed in {#initialize})
|
72
|
+
#
|
73
|
+
# Subclasses can override this method for pre/post scan processing, calling super as required
|
74
|
+
def scan()
|
75
|
+
db.execute(@sql) do |row|
|
76
|
+
new_path, real_path, options = map_row(row)
|
77
|
+
options ||= {}
|
78
|
+
options[:sqlite_scan_id] = @scan_id
|
79
|
+
map_file(new_path, real_path, options)
|
80
|
+
end
|
81
|
+
cleanup() { |file_node| file_node.options[:sqlite_scan_id] != @scan_id }
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def start_notifier
|
87
|
+
notifier = INotify::Notifier.new()
|
88
|
+
modified = false
|
89
|
+
notifier.watch(@db_path,:modify, :close_write) do |event|
|
90
|
+
modified = true if event.flags.include?(:modify)
|
91
|
+
if event.flags.include?(:close_write) && modified
|
92
|
+
@mutex.synchronize {@cv.signal}
|
93
|
+
modified = false
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
Thread.new { notifier.run }
|
98
|
+
|
99
|
+
notifier
|
100
|
+
end
|
101
|
+
|
102
|
+
def scan_loop(notifier)
|
103
|
+
@mutex.synchronize() do
|
104
|
+
@scan_id = 0
|
105
|
+
while @mounted
|
106
|
+
begin
|
107
|
+
@db = SQLite3::Database.new(@db_path,:readonly => true)
|
108
|
+
@db.results_as_hash = true
|
109
|
+
@db.busy_timeout(10000)
|
110
|
+
@scan_id = @scan_id + 1
|
111
|
+
scan()
|
112
|
+
rescue StandardError => e
|
113
|
+
puts e
|
114
|
+
puts e.backtrace.join("\n")
|
115
|
+
ensure
|
116
|
+
@db.close unless @db.closed?
|
117
|
+
@db = nil
|
118
|
+
end
|
119
|
+
@cv.wait(@mutex)
|
120
|
+
end
|
121
|
+
notifier.stop
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/lib/rfusefs/version.rb
CHANGED