rfusefs 1.0.2.RC2 → 1.1.1.rc20201114.37

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 56f162c21060ca29f73f4af1323d4e5913199fba944f63ae3de239467ef03898
4
+ data.tar.gz: 55e32321f1df3ad1c9f85f17b8b1a88fd2038d0fb713e7ba2996dc1f4e7c1546
5
+ SHA512:
6
+ metadata.gz: 02620a1786ab417c1653596a2ee952235b8a2fd14d92ee05d089514e2420e15d15844c7991bc9b08f30f889dac5c8871949b0bd718515f1ac9a2a998f7fec390
7
+ data.tar.gz: 3cec6f71911dfbf1dbdc65424cf2a61c13dd96b4e2226396afa1c7684e6981e1f38663dd1eb44df79746d9b1c5994def790601dddbccf8dbed4cfe01f318349a
@@ -0,0 +1,2 @@
1
+ -
2
+ CHANGES.md
@@ -0,0 +1,43 @@
1
+ 1.1.1 / 2020-11
2
+ ---------------
3
+ * fix gemspec
4
+
5
+ 1.1.0 / 2020-10
6
+ ---------------
7
+
8
+ * With rfuse ~> 1.2
9
+ * Requires Ruby 2.5+
10
+ * Release via Travis CI
11
+
12
+ 1.0.3 / 2014-12-22
13
+ ----------------------
14
+
15
+ * Pushed some internals up to RFuse
16
+ * Allow filesystems to implement signal handlers
17
+
18
+ 1.0.1 / 2013-12-19
19
+ ------------------
20
+
21
+ * Add FuseFS.main to create pretty usage messages
22
+ * Support extended attributes in filesystems
23
+ * Updates and cleanup of PathMapperFS
24
+ * Provide SqliteMapperFS
25
+
26
+ 1.0.0 / 2012-08-07
27
+ ------------------
28
+
29
+ * Depend on new rfuse 1.0.0, Ruby 1.9
30
+ * API breaking changes
31
+ * order of arguments to {FuseFS.mount}, {FuseFS.start} changed
32
+ to account for better option handling in RFuse
33
+
34
+ 0.8.0 / 2011-02-19
35
+ ------------------
36
+
37
+ * Initial port from fusefs
38
+
39
+ * Improved raw methods
40
+ * new "times" api for including atime,mtime,ctime in stat results
41
+ * metadir allow mv directories
42
+ * includes PathMapperFS
43
+
data/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ (The MIT License)
2
+
3
+ * Copyright (c) 2005 Greg Millam. (FuseFS)
4
+ * Copyright (c) 2009 Kyle Maxwell. (FuseFS)
5
+ * Copyright (c) 2012 - 2020 Grant Gardner. (RFuseFS)
6
+
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ of this software and associated documentation files (the 'Software'), to deal
10
+ in the Software without restriction, including without limitation the rights
11
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ copies of the Software, and to permit persons to whom the Software is
13
+ furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in all
16
+ copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ SOFTWARE.
@@ -0,0 +1,83 @@
1
+ # rfusefs
2
+
3
+ * https://rubygems.org/gems/rfusefs
4
+ * https://github.com/lwoggardner/rfusefs
5
+
6
+ [<img src="https://badge.fury.io/rb/rfusefs.png" alt="Gem Version"
7
+ />](http://badge.fury.io/rb/rfusefs)
8
+ ## DESCRIPTION
9
+
10
+ RFuseFS is a port of the [FuseFS](http://rubygems.org/gems/fusefs/) library
11
+ aimed at allowing Ruby programmers to quickly and easily create virtual
12
+ filesystems with little more than a few lines of code.
13
+
14
+ RFuseFS is api compatible with FuseFS (0.7.0)
15
+
16
+ ## SYNOPSIS
17
+
18
+ FuseFS provides a layer of abstraction to a programmer who wants to create a
19
+ virtual filesystem via FUSE.
20
+
21
+ First define a virtual directory by subclassing {FuseFS::FuseDir}
22
+
23
+ See samples under /samples and also the following starter classes
24
+
25
+ * {FuseFS::FuseDir}
26
+ * {FuseFS::MetaDir}
27
+ * {FuseFS::DirLink}
28
+ * {FuseFS::PathMapperFS}
29
+ * {FuseFS::SqliteMapperFS}
30
+
31
+
32
+ Then start your filesystem with
33
+
34
+ * {FuseFS.main} or {FuseFS.start}
35
+
36
+
37
+ Finally to use the filesystem open up your favourite file browser/terminal and
38
+ explore the contents under <mountpoint>
39
+
40
+ Happy Filesystem Hacking!
41
+
42
+ ### the hello world filesystem in 14 LOC
43
+
44
+ require 'rfusefs'
45
+
46
+ class HelloDir
47
+
48
+ def contents(path)
49
+ ['hello.txt']
50
+ end
51
+
52
+ def file?(path)
53
+ path == '/hello.txt'
54
+ end
55
+
56
+ def read_file(path)
57
+ "Hello, World!\n"
58
+ end
59
+
60
+ end
61
+
62
+ # Usage: #{$0} mountpoint [mount_options]
63
+ FuseFS.main() { |options| HelloDir.new }
64
+
65
+ ## REQUIREMENTS:
66
+
67
+ * FUSE (http://fuse.sourceforge.net)
68
+ * Ruby (>= 2.5)
69
+ * rfuse (~> 1.2)
70
+
71
+
72
+ ## INSTALL:
73
+
74
+ * gem install rfusefs
75
+
76
+ ## DEVELOPERS:
77
+
78
+ After checking out the source, run:
79
+
80
+ $ bundle install # install dependencies
81
+ $ rake spec # run tests
82
+ $ rake yard # generate docs
83
+
data/TODO.md ADDED
@@ -0,0 +1,7 @@
1
+
2
+ TODO
3
+ --------------
4
+
5
+ * Test (or remove) samples
6
+
7
+
@@ -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)
@@ -258,7 +272,7 @@ module FuseFS
258
272
  # @param [Integer] size
259
273
  # @param [Object] raw the filehandle returned by {#raw_open}
260
274
  # @abstract FuseFS api
261
- # @return [void]
275
+ # @return [String] _sz_ bytes contents from file at path (or filehandle raw) starting at offset off
262
276
  def raw_read(path,offset,size,raw=nil);end
263
277
 
264
278
  # Write _sz_ bytes from file at path (or filehandle raw) starting at offset off
@@ -269,7 +283,7 @@ module FuseFS
269
283
 
270
284
  # Sync buffered data to your filesystem
271
285
  # @param [String] path
272
- # @param [Boolena] datasync only sync user data, not metadata
286
+ # @param [Boolean] datasync only sync user data, not metadata
273
287
  # @param [Object] raw the filehandle return by {#raw_open}
274
288
  def raw_sync(path,datasync,raw=nil);end
275
289
 
@@ -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()
@@ -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
- #Which raw api should we use?
7
- RFUSEFS_COMPATIBILITY = true unless FuseFS.const_defined?(:RFUSEFS_COMPATIBILITY)
8
-
9
- class FileHandle
10
- @@fh = 0
11
- attr_reader :id,:flags,:path
12
- attr_accessor :raw,:contents
13
- def initialize(path,flags)
14
- @id = (@@fh += 1)
15
- @flags = flags
16
- @path = path
17
- @modified = false
18
- @contents = ""
19
- @size = 0
20
- end
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
- def read(offset,size)
23
- contents[offset,size]
24
- end
23
+ def read(offset,size)
24
+ contents[offset,size]
25
+ end
25
26
 
26
- def create
27
- @contents = ""
28
- @modified = true
29
- end
27
+ def create
28
+ @contents = ""
29
+ @modified = true
30
+ end
30
31
 
31
- def write(offset,data)
32
- # TODO: why append?
33
- if append? || offset >= contents.length
34
- #ignore offset
35
- #TODO: should this zero fill?
36
- contents << data
37
- else
38
- contents[offset,data.length]=data
39
- end
40
- @modified = true
41
- return data.length
42
- end
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
- def flush
45
- @modified = false
46
- contents
47
- end
45
+ def flush
46
+ @modified = false
47
+ contents
48
+ end
48
49
 
49
- def modified?
50
- @modified
51
- end
50
+ def modified?
51
+ @modified
52
+ end
52
53
 
53
- def accmode
54
- flags & Fcntl::O_ACCMODE
55
- end
54
+ def accmode
55
+ flags & Fcntl::O_ACCMODE
56
+ end
56
57
 
57
- def rdwr?
58
- accmode == Fcntl::O_RDWR
59
- end
58
+ def rdwr?
59
+ accmode == Fcntl::O_RDWR
60
+ end
60
61
 
61
- def wronly?
62
- accmode == Fcntl::O_WRONLY
63
- end
62
+ def wronly?
63
+ accmode == Fcntl::O_WRONLY
64
+ end
64
65
 
65
- def rdonly?
66
- accmode == Fcntl::O_RDONLY
67
- end
66
+ def rdonly?
67
+ accmode == Fcntl::O_RDONLY
68
+ end
68
69
 
69
- def append?
70
- writing? && (flags & Fcntl::O_APPEND != 0)
71
- end
70
+ def append?
71
+ writing? && (flags & Fcntl::O_APPEND != 0)
72
+ end
72
73
 
73
- def reading?
74
- rdonly? || rdwr?
75
- end
74
+ def reading?
75
+ rdonly? || rdwr?
76
+ end
76
77
 
77
- def writing?
78
- wronly? || rdwr?
79
- end
78
+ def writing?
79
+ wronly? || rdwr?
80
+ end
80
81
 
81
- def raw_mode
82
- mode_str = case accmode
83
- when Fcntl::O_RDWR; "rw"
84
- when Fcntl::O_RDONLY; "r"
85
- when Fcntl::O_WRONLY; "w"
86
- end
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
- mode_str << "a" if append?
89
- return mode_str
90
- end
89
+ mode_str << "a" if append?
90
+ return mode_str
91
91
  end
92
+ end
92
93
 
93
- # Implements RFuseFS
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 RFuseFS
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
- return wrap_context(ctx,__method__,path,filler,offset,ffi) if ctx
116
+ # We forward some methods
117
+ include Forwardable
121
118
 
122
- #Always have "." and ".."
123
- filler.push(".",nil,0)
124
- filler.push("..",nil,0)
119
+ CHECK_FILE="/._rfuse_check_"
125
120
 
126
- files = @root.contents(path)
121
+ def initialize(root)
122
+ @root = root
123
+ @created_files = { }
127
124
 
128
- files.each do | filename |
129
- filler.push(filename,nil,0)
130
- end
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
- def getattr(ctx,path)
135
-
136
- return wrap_context(ctx,__method__,path) if ctx
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
- end #getattr
143
+ def readdir(ctx,path,filler,offset,ffi)
164
144
 
165
- def mkdir(ctx,path,mode)
145
+ return wrap_context(ctx,__method__,path,filler,offset,ffi) if ctx
166
146
 
167
- return wrap_context(ctx,__method__,path,mode) if ctx
147
+ #Always have "." and ".."
148
+ filler.push(".",nil,0)
149
+ filler.push("..",nil,0)
168
150
 
169
- unless @root.can_mkdir?(path)
170
- raise Errno::EACCES.new(path)
171
- end
151
+ files = @root.contents(path)
172
152
 
173
- @root.mkdir(path)
174
- @adj_nodes += 1
175
- end #mkdir
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
- now = Time.now
186
- stat = RFuse::Stat.file(mode,{ :uid => Process.uid, :gid => Process.gid, :atime => now, :mtime => now, :ctime => now })
157
+ end
187
158
 
188
- @created_files[path] = stat
189
- @adj_nodes += 1
190
- end #mknod
159
+ def getattr(ctx,path)
191
160
 
192
- #ftruncate - eg called after opening a file for write without append
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
- return wrap_context(ctx,__method__,path,offset,ffi) if ctx
163
+ uid = Process.uid
164
+ gid = Process.gid
197
165
 
198
- fh = ffi.fh
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
- if fh.raw
201
- @root.raw_truncate(path,offset,fh.raw)
202
- if (offset <= 0)
203
- fh.contents = ""
204
- else
205
- fh.contents = fh.contents[0..offset]
206
- end
207
- end
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
- #truncate a file outside of open files
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
- current_size = size(path)
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
- unless fh.raw
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
- #If we get this far, save our filehandle in the FUSE structure
268
- ffi.fh=fh
194
+ unless @root.can_mkdir?(path)
195
+ raise Errno::EACCES.new(path)
269
196
  end
270
197
 
271
- def read(ctx,path,size,offset,ffi)
272
- return wrap_context(ctx,__method__,path,size,offset,ffi) if ctx
198
+ @root.mkdir(path)
199
+ @adj_nodes += 1
200
+ end #mkdir
273
201
 
274
- fh = ffi.fh
202
+ def mknod(ctx,path,mode,major,minor)
275
203
 
276
- if fh.raw
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
- def write(ctx,path,buf,offset,ffi)
293
- return wrap_context(ctx,__method__,path,buf,offset,ffi) if ctx
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
- def fsync(ctx,path,datasync,ffi)
309
- return wrap_context(ctx,__method__,path,datasync,ffi) if ctx
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
- if fh && fh.raw
313
- if FuseFS::RFUSEFS_COMPATIBILITY
314
- @root.raw_sync(path,datasync != 0,fh.raw)
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
- def flush(ctx,path,ffi)
324
- return wrap_context(ctx,__method__,path,ffi) if ctx
325
- fh = ffi.fh
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
- if fh && !fh.raw && fh.modified?
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
- def release(ctx,path,ffi)
336
- return wrap_context(ctx,__method__,path,ffi) if ctx
223
+ fh = ffi.fh
337
224
 
338
-
339
- fh = ffi.fh
340
- if fh && fh.raw
341
- if (FuseFS::RFUSEFS_COMPATIBILITY)
342
- @root.raw_close(path,fh.raw)
343
- else
344
- @root.raw_close(path)
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
- #def chmod(path,mode)
355
- #end
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
- #Touch...
364
- @root.touch(path,modtime) if @root.respond_to?(:touch)
239
+ unless @root.can_write?(path)
240
+ raise Errno::EACESS.new(path)
365
241
  end
366
242
 
367
- def unlink(ctx,path)
368
- return wrap_context(ctx,__method__,path) if ctx
369
-
370
- unless @root.can_delete?(path)
371
- raise Errno::EACCES.new(path)
372
- end
373
-
374
- @adj_size = @adj_size - size(path)
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
- def rmdir(ctx,path)
381
- return wrap_context(ctx,__method__,path) if ctx
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
- unless @root.can_rmdir?(path)
384
- raise Errno::EACCES.new(path)
385
- end
386
- @root.rmdir(path)
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
- #def symlink(path,as)
390
- #end
391
-
392
- def rename(ctx,from,to)
393
- return wrap_context(ctx,__method__,from,to) if ctx
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 @root.rename(from,to)
396
- # nothing to do
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
- raise Errno::EACCES.new("Unable to move directory #{from}")
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
- 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
405
300
 
406
- #def link(path,as)
407
- #end
408
-
409
- def setxattr(ctx,path,name,value,flags)
410
- return wrap_context(ctx,__method__,path,name,value,flags) if ctx
411
- @root.xattr(path)[name]=value
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
- def getxattr(ctx,path,name)
415
- return wrap_context(ctx,__method__,path,name) if ctx
416
- result = @root.xattr(path)[name]
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
- def listxattr(ctx,path)
422
- return wrap_context(ctx,__method__,path) if ctx
423
- @root.xattr(path).keys
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
- def removexattr(ctx,path,name)
427
- return wrap_context(ctx,__method__,path,name) if ctx
428
- @root.xattr(path).delete(name)
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
- #def opendir(path,ffi)
432
- #end
433
-
434
- #def releasedir(path,ffi)
435
- #end
436
-
437
- #
438
- #def fsyncdir(path,meta,ffi)
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
- def mounted()
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
- end
505
+ end
481
506
 
482
- def unmounted()
507
+ def unmounted()
483
508
  @root.unmounted()
484
- end
509
+ end
485
510
 
486
- def self.context(ctx,&block)
511
+ def self.context(ctx,&block)
487
512
  begin
488
- Thread.current[:fusefs_reader_uid] = ctx.uid
489
- Thread.current[:fusefs_reader_gid] = ctx.gid
490
- yield
513
+ Thread.current[:fusefs_reader_uid] = ctx.uid
514
+ Thread.current[:fusefs_reader_gid] = ctx.gid
515
+ yield
491
516
  ensure
492
- Thread.current[:fusefs_reader_uid] = nil
493
- Thread.current[:fusefs_reader_gid] = nil
517
+ Thread.current[:fusefs_reader_uid] = nil
518
+ Thread.current[:fusefs_reader_gid] = nil
494
519
  end
495
- end
520
+ end
521
+
522
+ # @private - no doc
523
+ def to_s
524
+ "RFuseFS::#{@root}"
525
+ end
496
526
 
497
- private
527
+ private
498
528
 
499
- def wrap_context(ctx,method,*args)
529
+ def wrap_context(ctx,method,*args)
500
530
  self.class.context(ctx) { send(method,nil,*args) }
501
- end
531
+ end
502
532
 
503
- def size(path)
533
+ def size(path)
504
534
  @root.respond_to?(:size) ? @root.size(path) : @root.read_file(path).length
505
- end
535
+ end
536
+
537
+ end #class Root
506
538
 
507
- end #class RFuseFS
539
+ end #class Fuse
508
540
  end #Module FuseFS