ruby_smb 3.1.1 → 3.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +1 -2
  3. data/examples/file_server.rb +8 -1
  4. data/examples/virtual_file_server.rb +143 -0
  5. data/lib/ruby_smb/fscc/file_information/file_access_information.rb +15 -0
  6. data/lib/ruby_smb/fscc/file_information/file_alignment_information.rb +45 -0
  7. data/lib/ruby_smb/fscc/file_information/file_all_information.rb +23 -0
  8. data/lib/ruby_smb/fscc/file_information/file_basic_information.rb +20 -0
  9. data/lib/ruby_smb/fscc/file_information/file_both_directory_information.rb +3 -3
  10. data/lib/ruby_smb/fscc/file_information/file_directory_information.rb +3 -3
  11. data/lib/ruby_smb/fscc/file_information/file_ea_information.rb +1 -0
  12. data/lib/ruby_smb/fscc/file_information/file_full_directory_information.rb +3 -3
  13. data/lib/ruby_smb/fscc/file_information/file_id_both_directory_information.rb +3 -3
  14. data/lib/ruby_smb/fscc/file_information/file_id_full_directory_information.rb +3 -3
  15. data/lib/ruby_smb/fscc/file_information/file_internal_information.rb +15 -0
  16. data/lib/ruby_smb/fscc/file_information/file_mode_information.rb +29 -0
  17. data/lib/ruby_smb/fscc/file_information/file_name_information.rb +16 -0
  18. data/lib/ruby_smb/fscc/file_information/file_names_information.rb +1 -1
  19. data/lib/ruby_smb/fscc/file_information/file_normalized_name_information.rb +16 -0
  20. data/lib/ruby_smb/fscc/file_information/file_position_information.rb +15 -0
  21. data/lib/ruby_smb/fscc/file_information/file_rename_information.rb +1 -1
  22. data/lib/ruby_smb/fscc/file_information/file_standard_information.rb +20 -0
  23. data/lib/ruby_smb/fscc/file_information/file_stream_information.rb +3 -0
  24. data/lib/ruby_smb/fscc/file_information.rb +41 -8
  25. data/lib/ruby_smb/fscc/file_system_information/file_fs_attribute_information.rb +1 -0
  26. data/lib/ruby_smb/fscc/file_system_information/file_fs_volume_information.rb +1 -0
  27. data/lib/ruby_smb/fscc/file_system_information.rb +4 -0
  28. data/lib/ruby_smb/gss/provider/ntlm.rb +16 -3
  29. data/lib/ruby_smb/gss/provider.rb +10 -1
  30. data/lib/ruby_smb/server/server_client/session_setup.rb +4 -2
  31. data/lib/ruby_smb/server/server_client.rb +9 -1
  32. data/lib/ruby_smb/server/session.rb +5 -1
  33. data/lib/ruby_smb/server/share/provider/disk/processor/close.rb +9 -5
  34. data/lib/ruby_smb/server/share/provider/disk/processor/create.rb +2 -2
  35. data/lib/ruby_smb/server/share/provider/disk/processor/query.rb +2 -2
  36. data/lib/ruby_smb/server/share/provider/disk/processor/read.rb +15 -14
  37. data/lib/ruby_smb/server/share/provider/disk/processor.rb +76 -12
  38. data/lib/ruby_smb/server/share/provider/disk.rb +8 -2
  39. data/lib/ruby_smb/server/share/provider/virtual_disk/virtual_file.rb +85 -0
  40. data/lib/ruby_smb/server/share/provider/virtual_disk/virtual_pathname.rb +196 -0
  41. data/lib/ruby_smb/server/share/provider/virtual_disk/virtual_stat.rb +175 -0
  42. data/lib/ruby_smb/server/share/provider/virtual_disk.rb +116 -0
  43. data/lib/ruby_smb/server/share/provider.rb +1 -0
  44. data/lib/ruby_smb/server.rb +13 -3
  45. data/lib/ruby_smb/smb2/tree.rb +1 -0
  46. data/lib/ruby_smb/version.rb +1 -1
  47. data/spec/lib/ruby_smb/fscc/file_information/file_access_information_spec.rb +21 -0
  48. data/spec/lib/ruby_smb/fscc/file_information/file_alignment_information_spec.rb +21 -0
  49. data/spec/lib/ruby_smb/fscc/file_information/file_all_information_spec.rb +61 -0
  50. data/spec/lib/ruby_smb/fscc/file_information/file_basic_information_spec.rb +41 -0
  51. data/spec/lib/ruby_smb/fscc/file_information/file_both_directory_information_spec.rb +59 -10
  52. data/spec/lib/ruby_smb/fscc/file_information/file_directory_information_spec.rb +30 -12
  53. data/spec/lib/ruby_smb/fscc/file_information/file_ea_information_spec.rb +21 -0
  54. data/spec/lib/ruby_smb/fscc/file_information/file_full_directory_information_spec.rb +30 -12
  55. data/spec/lib/ruby_smb/fscc/file_information/file_id_both_directory_information_spec.rb +63 -10
  56. data/spec/lib/ruby_smb/fscc/file_information/file_id_full_directory_information_spec.rb +30 -12
  57. data/spec/lib/ruby_smb/fscc/file_information/file_internal_information_spec.rb +21 -0
  58. data/spec/lib/ruby_smb/fscc/file_information/file_mode_information_spec.rb +21 -0
  59. data/spec/lib/ruby_smb/fscc/file_information/file_name_information_spec.rb +44 -0
  60. data/spec/lib/ruby_smb/fscc/file_information/file_names_information_spec.rb +30 -12
  61. data/spec/lib/ruby_smb/fscc/file_information/file_network_open_information_spec.rb +51 -0
  62. data/spec/lib/ruby_smb/fscc/file_information/file_normalized_name_information_spec.rb +44 -0
  63. data/spec/lib/ruby_smb/fscc/file_information/file_position_information_spec.rb +21 -0
  64. data/spec/lib/ruby_smb/fscc/file_information/file_rename_information_spec.rb +1 -1
  65. data/spec/lib/ruby_smb/fscc/file_information/file_standard_information_spec.rb +41 -0
  66. data/spec/lib/ruby_smb/fscc/file_information/file_stream_information_spec.rb +51 -0
  67. data/spec/lib/ruby_smb/fscc/file_information_spec.rb +14 -0
  68. data/spec/lib/ruby_smb/fscc/file_system_information/file_fs_attribute_information_spec.rb +46 -0
  69. data/spec/lib/ruby_smb/fscc/file_system_information/file_fs_volume_information_spec.rb +51 -0
  70. data/spec/lib/ruby_smb/fscc/file_system_information_spec.rb +14 -0
  71. data/spec/lib/ruby_smb/server/server_client_spec.rb +15 -0
  72. data/spec/lib/ruby_smb/server/share/provider/virtual_disk/virtual_pathname_spec.rb +581 -0
  73. data/spec/lib/ruby_smb/server/share/provider/virtual_disk/virtual_stat_spec.rb +207 -0
  74. data/spec/lib/ruby_smb/server/share/provider/virtual_disk_spec.rb +122 -0
  75. data.tar.gz.sig +0 -0
  76. metadata +57 -2
  77. metadata.gz.sig +0 -0
@@ -0,0 +1,196 @@
1
+ module RubySMB
2
+ class Server
3
+ module Share
4
+ module Provider
5
+ class VirtualDisk < Disk
6
+ # This object emulates Ruby's builtin Pathname object but uses a virtual file system instead of the real local
7
+ # one.
8
+ class VirtualPathname
9
+ SEPARATOR = File::SEPARATOR
10
+ # see: https://ruby-doc.org/stdlib-3.1.1/libdoc/pathname/rdoc/Pathname.html
11
+ STAT_METHODS = %i[
12
+ atime
13
+ birthtime
14
+ blockdev?
15
+ chardev?
16
+ ctime
17
+ directory?
18
+ executable?
19
+ file?
20
+ ftype
21
+ grpowned?
22
+ mtime
23
+ owned?
24
+ pipe?
25
+ readable?
26
+ setgid?
27
+ setuid?
28
+ size
29
+ socket?
30
+ sticky?
31
+ symlink?
32
+ world_readable?
33
+ world_writable?
34
+ writable?
35
+ zero?
36
+ ]
37
+ private_constant :STAT_METHODS
38
+
39
+ attr_accessor :virtual_disk
40
+
41
+ # @param [Hash] disk The mapping of paths to objects representing the virtual file system.
42
+ # @param [String] path The path of this entry.
43
+ # @param [Boolean] exist? Whether or not this entry represents an existing entry in the virtual file system.
44
+ # @param [File::Stat] stat An explicit stat that represents this object. A default VirtualStat will be
45
+ # created unless specified.
46
+ def initialize(disk, path, **kwargs)
47
+ @virtual_disk = disk
48
+ @path = path
49
+
50
+ if kwargs.fetch(:exist?, true)
51
+ if kwargs[:stat]
52
+ if kwargs[:stat].is_a?(Hash)
53
+ @stat = VirtualStat.new(**kwargs[:stat])
54
+ else
55
+ @stat = kwargs[:stat]
56
+ end
57
+ else
58
+ @stat = VirtualStat.new
59
+ end
60
+ else
61
+ raise ArgumentError.new('can not specify a stat object when exist? is false') if kwargs[:stat]
62
+ @stat = nil
63
+ end
64
+ end
65
+
66
+ def ==(other)
67
+ other.is_a?(self.class) && other.to_s == to_s
68
+ end
69
+
70
+ def <=>(other)
71
+ to_s <=> other.to_s
72
+ end
73
+
74
+ def exist?
75
+ !@stat.nil?
76
+ rescue Errno::ENOENT
77
+ false
78
+ end
79
+
80
+ def stat
81
+ raise Errno::ENOENT.new('No such file or directory') unless exist? && (@stat.file? || @stat.directory?)
82
+
83
+ @stat
84
+ end
85
+
86
+ def join(other)
87
+ # per the docs this Pathname#join doesn't touch the file system
88
+ # see: https://ruby-doc.org/stdlib-3.1.1/libdoc/pathname/rdoc/Pathname.html#class-Pathname-label-Core+methods
89
+ lookup_or_create(Pathname.new(to_s).join(other).to_s)
90
+ end
91
+
92
+ alias :+ :join
93
+ alias :/ :join
94
+
95
+ def to_s
96
+ @path
97
+ end
98
+
99
+ def absolute?
100
+ to_s.start_with?(SEPARATOR)
101
+ end
102
+
103
+ def relative?
104
+ !absolute?
105
+ end
106
+
107
+ def basename
108
+ lookup_or_create(self.class.basename(to_s))
109
+ end
110
+
111
+ def self.basename(*args)
112
+ File.basename(*args)
113
+ end
114
+
115
+ def dirname
116
+ lookup_or_create(self.class.dirname(to_s))
117
+ end
118
+
119
+ def self.dirname(*args)
120
+ File.dirname(*args)
121
+ end
122
+
123
+ def extname
124
+ File.extname(to_s)
125
+ end
126
+
127
+ def split
128
+ [dirname, basename]
129
+ end
130
+
131
+ alias :parent :dirname
132
+
133
+ def children(with_directory=true)
134
+ raise Errno::ENOTDIR.new("Not a directory @ dir_initialize - #{to_s}") unless directory?
135
+
136
+ @virtual_disk.each_value.select { |dent| dent.dirname == self && dent != self }.map { |dent| with_directory ? dent : dent.basename }
137
+ end
138
+
139
+ def entries
140
+ children(false)
141
+ end
142
+
143
+ def cleanpath(consider_symlink=false)
144
+ lookup_or_create(self.class.cleanpath(to_s), stat: (exist? ? stat : nil))
145
+ end
146
+
147
+ def self.cleanpath(path)
148
+ # per the docs this Pathname#cleanpath doesn't touch the file system
149
+ # see: https://ruby-doc.org/stdlib-3.1.1/libdoc/pathname/rdoc/Pathname.html#class-Pathname-label-Core+methods
150
+ Pathname.new(path).cleanpath.to_s
151
+ end
152
+
153
+ private
154
+
155
+ # Check the virtual file system to see if the entry exists. Return it if it does, otherwise create a new
156
+ # entry representing a non-existent path.
157
+ #
158
+ # @param [String] path The path to lookup in the virtual file system. It will be normalized using #cleanpath.
159
+ # @return [Pathname] The path object representing the specified string.
160
+ def lookup_or_create(path, **kwargs)
161
+ existing = @virtual_disk[self.class.cleanpath(path)]
162
+ return existing if existing
163
+
164
+ kwargs[:exist?] = false
165
+ VirtualPathname.new(@virtual_disk, path, **kwargs)
166
+ end
167
+
168
+ def method_missing(symbol, *args)
169
+ # should we forward to one of the stat methods
170
+ if STAT_METHODS.include?(symbol)
171
+ # if we have a stat object then forward it
172
+ return stat.send(symbol, *args) if exist?
173
+ # if we don't have a stat object, emulate what Pathname does when it does not exist
174
+
175
+ # these two methods return nil
176
+ return nil if %i[ world_readable? world_writable? ].include?(symbol)
177
+
178
+ # any of the other ?-suffixed methods return false
179
+ return false if symbol.to_s.end_with?('?')
180
+
181
+ # any other method raises a Errno::ENOENT exception
182
+ raise Errno::ENOENT.new('No such file or directory')
183
+ end
184
+
185
+ raise NoMethodError, "undefined method `#{symbol}' for #{self.class}"
186
+ end
187
+
188
+ def respond_to_missing?(symbol, include_private = false)
189
+ STAT_METHODS.include?(symbol)
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,175 @@
1
+ module RubySMB
2
+ class Server
3
+ module Share
4
+ module Provider
5
+ class VirtualDisk < Disk
6
+ # This object emulates Ruby's builtin File::Stat object but uses a virtual file system instead of the real
7
+ # local one. The current implementation is limited to only representing directories and standard files. All
8
+ # attributes are read-only and once the object is created, it is immutable.
9
+ class VirtualStat
10
+
11
+ # All of the keyword arguments are the keys of the attributes to set. The names are left as is, maintaining
12
+ # a direct 1 to 1 relationship. See the Ruby docs for File::Stat
13
+ # (https://ruby-doc.org/core-3.0.2/File/Stat.html) for a list of all the attributes that can be set. Some
14
+ # values are calculated based on others such as the mode readable? being calculated based on the mode.
15
+ def initialize(**kwargs)
16
+ # directory and file both default to being the opposite of each other, one or both can be specified
17
+ # but they can not both be true at the same time
18
+ is_dir = !!kwargs.fetch(:directory?, !kwargs.fetch(:file?, false)) # defaults to not file which defaults to false
19
+ is_fil = !!kwargs.fetch(:file?, !kwargs.fetch(:directory?, true)) # defaults to not directory which defaults to true
20
+ raise ArgumentError.new('must be either a file or a directory') unless is_dir ^ is_fil
21
+
22
+ @values = kwargs.dup
23
+ # the default is a directory
24
+ @values[:directory?] = !@values.delete(:file?) if @values.key?(:file?) # normalize on directory? if file? was specified.
25
+
26
+ @birthtime = kwargs[:birthtime] || Time.now
27
+ end
28
+
29
+ def blksize
30
+ @values.fetch(:blksize, 4096)
31
+ end
32
+
33
+ def blockdev?
34
+ false
35
+ end
36
+
37
+ def blocks
38
+ @values.fetch(:blocks, 0)
39
+ end
40
+
41
+ def chardev?
42
+ false
43
+ end
44
+
45
+ def pipe?
46
+ false
47
+ end
48
+
49
+ def socket?
50
+ false
51
+ end
52
+
53
+ def symlink?
54
+ false
55
+ end
56
+
57
+ def directory?
58
+ @values.fetch(:directory?, true)
59
+ end
60
+
61
+ def file?
62
+ !directory?
63
+ end
64
+
65
+ def ftype
66
+ raise Errno::ENOENT.new('No such file or directory') unless file? || directory?
67
+
68
+ file? ? 'file' : 'directory'
69
+ end
70
+
71
+ def size
72
+ @values.fetch(:size, 0)
73
+ end
74
+
75
+ def zero?
76
+ file? && size == 0
77
+ end
78
+
79
+ def nlink
80
+ @values.fetch(:nlink, 0)
81
+ end
82
+
83
+ def dev
84
+ @values[:dev] ||= rand(1..0xfe)
85
+ end
86
+
87
+ def ino
88
+ @values[:ino] ||= rand(1..0xfffe)
89
+ end
90
+
91
+ def gid
92
+ @values.fetch(:gid, Process.gid)
93
+ end
94
+
95
+ def grpowned?
96
+ gid == Process.gid
97
+ end
98
+
99
+ def uid
100
+ @values.fetch(:uid, Process.uid)
101
+ end
102
+
103
+ def owned?
104
+ uid == Process.uid
105
+ end
106
+
107
+ # last access time
108
+ def atime
109
+ @values.fetch(:atime, @birthtime)
110
+ end
111
+
112
+ # modification time
113
+ def mtime
114
+ @values.fetch(:mtime, @birthtime)
115
+ end
116
+
117
+ # change time
118
+ def ctime
119
+ @values.fetch(:ctime, @birthtime)
120
+ end
121
+
122
+ # the permission bits, normalized based on the standard GNU representation,
123
+ # see: https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html
124
+ def mode
125
+ @values.fetch(:mode, (file? ? 0o644 : 0o755))
126
+ end
127
+
128
+ def setuid?
129
+ mode & 0o04000 != 0
130
+ end
131
+
132
+ def setgid?
133
+ mode & 0o02000 != 0
134
+ end
135
+
136
+ def sticky?
137
+ mode & 0o01000 != 0
138
+ end
139
+
140
+ def readable?
141
+ return true if owned? && (mode & 1 << 8 != 0)
142
+ return true if grpowned? && (mode & 1 << 5 != 0)
143
+ return true if world_readable?
144
+ return false
145
+ end
146
+
147
+ def world_readable?
148
+ mode & 1 << 2 != 0
149
+ end
150
+
151
+ def writable?
152
+ return true if owned? && (mode & 1 << 7 != 0)
153
+ return true if grpowned? && (mode & 1 << 4 != 0)
154
+ return true if world_writable?
155
+ return false
156
+ end
157
+
158
+ def world_writable?
159
+ mode & 1 << 1 != 0
160
+ end
161
+
162
+ def executable?
163
+ return true if owned? && (mode & 1 << 6 != 0)
164
+ return true if grpowned? && (mode & 1 << 3 != 0)
165
+ return true if mode & 1 != 0
166
+ return false
167
+ end
168
+
169
+ attr_reader :birthtime
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,116 @@
1
+ require 'ruby_smb/server/share/provider/disk'
2
+ require 'ruby_smb/server/share/provider/virtual_disk/virtual_file'
3
+ require 'ruby_smb/server/share/provider/virtual_disk/virtual_pathname'
4
+ require 'ruby_smb/server/share/provider/virtual_disk/virtual_stat'
5
+
6
+ module RubySMB
7
+ class Server
8
+ module Share
9
+ module Provider
10
+ # This is a share provider that exposes a virtual file system whose entries do not exist on disk.
11
+ # @since 3.1.1
12
+ class VirtualDisk < Disk
13
+ HASH_METHODS = %i[ [] each each_key each_value keys length values ]
14
+ private_constant :HASH_METHODS
15
+
16
+ # @param [String] name The name of this share.
17
+ def initialize(name)
18
+ @vfs = {}
19
+ super(name, add(VirtualPathname.new(self, File::SEPARATOR)))
20
+ end
21
+
22
+ # Add a dynamic file to the virtual file system. A dynamic file is one whose contents are generated by the
23
+ # specified block. The contents are generated when the share is opened.
24
+ #
25
+ # @param [String] path The path of the file to add, relative to the share root.
26
+ # @param [File::Stat] stat An explicit stat object describing the file.
27
+ # @yield The content generation routine.
28
+ # @yieldreturn [String] The generated file content.
29
+ def add_dynamic_file(path, stat: nil, &block)
30
+ raise ArgumentError.new('a block must be specified for dynamic files') unless block_given?
31
+ path = VirtualPathname.cleanpath(path)
32
+ path = File::SEPARATOR + path unless path.start_with?(File::SEPARATOR)
33
+ raise ArgumentError.new('must be a file') if stat && !stat.file?
34
+
35
+ vf = VirtualDynamicFile.new(self, path, stat: stat, &block)
36
+ add(vf)
37
+ end
38
+
39
+ # Add a mapped file to the virtual file system. A mapped file is one who is backed by an entry on
40
+ # disk. The path need not be present, but if it does exist, it must be a file.
41
+ #
42
+ # @param [String] path The path of the file to add, relative to the share root.
43
+ # @param [String, Pathname] mapped_path The path on the local file system to map into the virtual file system.
44
+ def add_mapped_file(path, mapped_path)
45
+ path = VirtualPathname.cleanpath(path)
46
+ path = File::SEPARATOR + path unless path.start_with?(File::SEPARATOR)
47
+
48
+ vf = VirtualMappedFile.new(self, path, mapped_path)
49
+ add(vf)
50
+ end
51
+
52
+ # Add a static file to the virtual file system. A static file is one whose contents are known at creation time
53
+ # and do not change. If *content* is a file-like object that responds to #read, the data will be read using
54
+ # that method. Likewise, if *content* has a #stat attribute and *stat* was not specified, then the value of
55
+ # content's #stat attribute will be used.
56
+ #
57
+ # @param [String] path The path of the file to add, relative to the share root.
58
+ # @param [String, #read, #stat] content The static content to add.
59
+ # @param [File::Stat] stat An explicit stat object describing the file.
60
+ def add_static_file(path, content, stat: nil)
61
+ path = VirtualPathname.cleanpath(path)
62
+ path = File::SEPARATOR + path unless path.start_with?(File::SEPARATOR)
63
+ raise ArgumentError.new('must be a file') if stat && !stat.file?
64
+
65
+ stat = content.stat if content.respond_to?(:stat) && stat.nil?
66
+ content = content.read if content.respond_to?(:read)
67
+ vf = VirtualStaticFile.new(self, path, content, stat: stat)
68
+ add(vf)
69
+ end
70
+
71
+ def add(virtual_pathname)
72
+ raise ArgumentError.new('paths must be absolute') unless virtual_pathname.absolute?
73
+
74
+ path = virtual_pathname.to_s
75
+ raise ArgumentError.new('paths must be normalized') unless VirtualPathname.cleanpath(path) == path
76
+
77
+ path_parts = path.split(VirtualPathname::SEPARATOR)
78
+ 2.upto(path_parts.length - 1) do |idx|
79
+ ancestor = path_parts[0...idx].join(path[VirtualPathname::SEPARATOR])
80
+ next if @vfs[ancestor]&.directory?
81
+
82
+ @vfs[ancestor] = VirtualPathname.new(self, ancestor, stat: VirtualStat.new(directory?: true))
83
+ end
84
+
85
+ @vfs[path] = virtual_pathname
86
+ end
87
+
88
+ def new_processor(server_client, session)
89
+ scoped_virtual_disk = self.class.new(@name)
90
+ @vfs.each_value do |path|
91
+ path = path.dup
92
+ path.virtual_disk = scoped_virtual_disk if path.is_a?(VirtualPathname)
93
+ path = path.generate(server_client, session) if path.is_a?(VirtualDynamicFile)
94
+ scoped_virtual_disk.add(path)
95
+ end
96
+ self.class::Processor.new(scoped_virtual_disk, server_client, session)
97
+ end
98
+
99
+ private
100
+
101
+ def method_missing(symbol, *args)
102
+ if HASH_METHODS.include?(symbol)
103
+ return @vfs.send(symbol, *args)
104
+ end
105
+
106
+ raise NoMethodError, "undefined method `#{symbol}' for #{self.class}"
107
+ end
108
+
109
+ def respond_to_missing?(symbol, include_private = false)
110
+ HASH_METHODS.include?(symbol)
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -36,3 +36,4 @@ end
36
36
 
37
37
  require 'ruby_smb/server/share/provider/disk'
38
38
  require 'ruby_smb/server/share/provider/pipe'
39
+ require 'ruby_smb/server/share/provider/virtual_disk'
@@ -14,8 +14,10 @@ module RubySMB
14
14
  Connection = Struct.new(:client, :thread)
15
15
 
16
16
  # @param server_sock the socket on which the server should listen
17
- # @param [Gss::Provider] the authentication provider
18
- def initialize(server_sock: nil, gss_provider: nil, logger: nil)
17
+ # @param [Gss::Provider] gss_provider the authentication provider
18
+ # @param [::Logger] logger the logger to use for diagnostic messages
19
+ # @param thread_factory a block to create threads for serving clients
20
+ def initialize(server_sock: nil, gss_provider: nil, logger: nil, thread_factory: nil)
19
21
  server_sock = ::TCPServer.new(445) if server_sock.nil?
20
22
 
21
23
  @guid = Random.new.bytes(16)
@@ -36,6 +38,14 @@ module RubySMB
36
38
  @logger = logger
37
39
  end
38
40
 
41
+ if thread_factory.nil?
42
+ # the default thread factory uses Ruby's standard Thread#new
43
+ thread_factory = Proc.new do |_server_client, &block|
44
+ Thread.new(&block)
45
+ end
46
+ end
47
+ @thread_factory = thread_factory
48
+
39
49
  # share name => provider instance
40
50
  @shares = {
41
51
  'IPC$' => Share::Provider::IpcPipe.new
@@ -53,7 +63,7 @@ module RubySMB
53
63
  loop do
54
64
  sock = @socket.accept
55
65
  server_client = ServerClient.new(self, RubySMB::Dispatcher::Socket.new(sock, read_timeout: nil))
56
- @connections << Connection.new(server_client, Thread.new { server_client.run })
66
+ @connections << Connection.new(server_client, @thread_factory.call(server_client) { server_client.run })
57
67
 
58
68
  break unless block.nil? || block.call(server_client)
59
69
  end
@@ -237,6 +237,7 @@ module RubySMB
237
237
 
238
238
  if read
239
239
  create_request.share_access.read_access = 1
240
+ create_request.desired_access.read_attr = 1
240
241
  create_request.desired_access.read_data = 1
241
242
  end
242
243
 
@@ -1,3 +1,3 @@
1
1
  module RubySMB
2
- VERSION = '3.1.1'.freeze
2
+ VERSION = '3.1.2'.freeze
3
3
  end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RubySMB::Fscc::FileInformation::FileAccessInformation do
4
+ it 'references the correct class level' do
5
+ expect(described_class).to be_const_defined(:CLASS_LEVEL)
6
+ expect(described_class::CLASS_LEVEL).to be RubySMB::Fscc::FileInformation::FILE_ACCESS_INFORMATION
7
+ end
8
+
9
+ subject(:struct) { described_class.new }
10
+
11
+ it { should respond_to :access_flags }
12
+
13
+ it 'is little endian' do
14
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
15
+ end
16
+
17
+ it 'tracks the access flags in a Uint32 field' do
18
+ expect(struct.access_flags).to be_a BinData::Uint32le
19
+ end
20
+
21
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RubySMB::Fscc::FileInformation::FileAlignmentInformation do
4
+ it 'references the correct class level' do
5
+ expect(described_class).to be_const_defined(:CLASS_LEVEL)
6
+ expect(described_class::CLASS_LEVEL).to be RubySMB::Fscc::FileInformation::FILE_ALIGNMENT_INFORMATION
7
+ end
8
+
9
+ subject(:struct) { described_class.new }
10
+
11
+ it { should respond_to :alignment_requirement }
12
+
13
+ it 'is little endian' do
14
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
15
+ end
16
+
17
+ it 'tracks the alignment requirement in a Uint32 field' do
18
+ expect(struct.alignment_requirement).to be_a BinData::Uint32le
19
+ end
20
+
21
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RubySMB::Fscc::FileInformation::FileAllInformation do
4
+ it 'references the correct class level' do
5
+ expect(described_class).to be_const_defined(:CLASS_LEVEL)
6
+ expect(described_class::CLASS_LEVEL).to be RubySMB::Fscc::FileInformation::FILE_ALL_INFORMATION
7
+ end
8
+
9
+ subject(:struct) { described_class.new }
10
+
11
+ it { should respond_to :basic_information }
12
+ it { should respond_to :standard_information }
13
+ it { should respond_to :internal_information }
14
+ it { should respond_to :ea_information }
15
+ it { should respond_to :access_information }
16
+ it { should respond_to :position_information }
17
+ it { should respond_to :mode_information }
18
+ it { should respond_to :alignment_information }
19
+ it { should respond_to :name_information }
20
+
21
+ it 'is little endian' do
22
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
23
+ end
24
+
25
+ it 'tracks the basic information in a FileBasicInformation field' do
26
+ expect(struct.basic_information).to be_a RubySMB::Fscc::FileInformation::FileBasicInformation
27
+ end
28
+
29
+ it 'tracks the standard information in a FileStandardInformation field' do
30
+ expect(struct.standard_information).to be_a RubySMB::Fscc::FileInformation::FileStandardInformation
31
+ end
32
+
33
+ it 'tracks the internal information in a FileInternalInformation field' do
34
+ expect(struct.internal_information).to be_a RubySMB::Fscc::FileInformation::FileInternalInformation
35
+ end
36
+
37
+ it 'tracks the ea information in a FileEaInformation field' do
38
+ expect(struct.ea_information).to be_a RubySMB::Fscc::FileInformation::FileEaInformation
39
+ end
40
+
41
+ it 'tracks the access information in a FileAccessInformation field' do
42
+ expect(struct.access_information).to be_a RubySMB::Fscc::FileInformation::FileAccessInformation
43
+ end
44
+
45
+ it 'tracks the position information in a FilePositionInformation field' do
46
+ expect(struct.position_information).to be_a RubySMB::Fscc::FileInformation::FilePositionInformation
47
+ end
48
+
49
+ it 'tracks the mode information in a FileModeInformation field' do
50
+ expect(struct.mode_information).to be_a RubySMB::Fscc::FileInformation::FileModeInformation
51
+ end
52
+
53
+ it 'tracks the alignment information in a FileAlignmentInformation field' do
54
+ expect(struct.alignment_information).to be_a RubySMB::Fscc::FileInformation::FileAlignmentInformation
55
+ end
56
+
57
+ it 'tracks the name information in a FileNameInformation field' do
58
+ expect(struct.name_information).to be_a RubySMB::Fscc::FileInformation::FileNameInformation
59
+ end
60
+ end
61
+
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RubySMB::Fscc::FileInformation::FileBasicInformation do
4
+ it 'references the correct class level' do
5
+ expect(described_class).to be_const_defined(:CLASS_LEVEL)
6
+ expect(described_class::CLASS_LEVEL).to be RubySMB::Fscc::FileInformation::FILE_BASIC_INFORMATION
7
+ end
8
+
9
+ subject(:struct) { described_class.new }
10
+
11
+ it { should respond_to :create_time }
12
+ it { should respond_to :last_access }
13
+ it { should respond_to :last_write }
14
+ it { should respond_to :last_change }
15
+ it { should respond_to :file_attributes }
16
+
17
+ it 'is little endian' do
18
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
19
+ end
20
+
21
+ it 'tracks the create time in a FileTime field' do
22
+ expect(struct.create_time).to be_a RubySMB::Field::FileTime
23
+ end
24
+
25
+ it 'tracks the last access time in a FileTime field' do
26
+ expect(struct.last_access).to be_a RubySMB::Field::FileTime
27
+ end
28
+
29
+ it 'tracks the last write time in a FileTime field' do
30
+ expect(struct.last_write).to be_a RubySMB::Field::FileTime
31
+ end
32
+
33
+ it 'tracks the last modified time in a FileTime field' do
34
+ expect(struct.last_change).to be_a RubySMB::Field::FileTime
35
+ end
36
+
37
+ it 'tracks the file attributes in a FileAttributes field' do
38
+ expect(struct.file_attributes).to be_a RubySMB::Fscc::FileAttributes
39
+ end
40
+
41
+ end