rfusefs 1.0.2 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- MzhlODUwNTJmNTNmNGM5OTZhNWU5Nzg0YjhkMTM2MWRmMmZmMzA5OQ==
5
- data.tar.gz: !binary |-
6
- YWRhODkyYzJjZGNjYzc2YzM4ZTc5NjJhYWUyNjlkM2FiYzNjMGI4Nw==
2
+ SHA256:
3
+ metadata.gz: 7b262bbb9de179cfa71cc8b7c6c1b2e879557f364c2c6f923176fb607b7abeb0
4
+ data.tar.gz: 29566f13a740362789004fa7d865f0a9e6c3a75cd0f70551033a9b7b9b2ea092
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- OGMwYWQ3YzE4MDJlZTlhNmExMWQxZWYyOGUzZTcxMWZhYWZkNzI3N2MwNmYz
10
- MWU1NTI5M2IyMjU2Y2Q2MjU3ZTQwNzcwN2EzYmQ2MzAwNzI1ZmFlNWJlYTll
11
- Y2FkMDY5NmNkYThjNmZhYWUzMDQ2ZWQ4YmRkMmMwNTRkNzYxMWY=
12
- data.tar.gz: !binary |-
13
- OTgxN2NmMWQ3MjBlZTQwODU4NTBlMTdiNjQ3NTMwNTZiYjUzMjUxMTZhZDg4
14
- MTllMGUwODliMGJjMDkzNTQwNDhiYTZhNzUzMzg0MjNiNmRjZWUwNGE1Yjdh
15
- NWY2NmE2YzYwNDg4NWJhNGRmYzI5MjZiYjhjZjZhNzUwOTZiZWM=
6
+ metadata.gz: ed3bf064022e69c006714709755d49848fda697bfdc1bf3b27126d9fdf7db1aee724ca71ddd3c44d621a8b8411904675c2f6e2b9559032cf229007ec7a5e2adf
7
+ data.tar.gz: cdcdde923e4ce54e0eafc909a67c275b57f8b354baa3fe3c02976163201fcfe0049b35d7e8ea00aa3b016dd4b60fe8fd76d0b97b1bb89bf74678aa3055265bdd
@@ -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