ruby_smb 3.1.0 → 3.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.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/examples/file_server.rb +6 -61
  4. data/examples/virtual_file_server.rb +91 -0
  5. data/lib/ruby_smb/client/negotiation.rb +7 -7
  6. data/lib/ruby_smb/client.rb +2 -2
  7. data/lib/ruby_smb/fscc/file_information/file_access_information.rb +15 -0
  8. data/lib/ruby_smb/fscc/file_information/file_alignment_information.rb +45 -0
  9. data/lib/ruby_smb/fscc/file_information/file_all_information.rb +23 -0
  10. data/lib/ruby_smb/fscc/file_information/file_basic_information.rb +20 -0
  11. data/lib/ruby_smb/fscc/file_information/file_both_directory_information.rb +3 -3
  12. data/lib/ruby_smb/fscc/file_information/file_directory_information.rb +3 -3
  13. data/lib/ruby_smb/fscc/file_information/file_ea_information.rb +1 -0
  14. data/lib/ruby_smb/fscc/file_information/file_full_directory_information.rb +3 -3
  15. data/lib/ruby_smb/fscc/file_information/file_id_both_directory_information.rb +3 -3
  16. data/lib/ruby_smb/fscc/file_information/file_id_full_directory_information.rb +3 -3
  17. data/lib/ruby_smb/fscc/file_information/file_internal_information.rb +15 -0
  18. data/lib/ruby_smb/fscc/file_information/file_mode_information.rb +29 -0
  19. data/lib/ruby_smb/fscc/file_information/file_name_information.rb +16 -0
  20. data/lib/ruby_smb/fscc/file_information/file_names_information.rb +1 -1
  21. data/lib/ruby_smb/fscc/file_information/file_normalized_name_information.rb +16 -0
  22. data/lib/ruby_smb/fscc/file_information/file_position_information.rb +15 -0
  23. data/lib/ruby_smb/fscc/file_information/file_rename_information.rb +1 -1
  24. data/lib/ruby_smb/fscc/file_information/file_standard_information.rb +20 -0
  25. data/lib/ruby_smb/fscc/file_information/file_stream_information.rb +3 -0
  26. data/lib/ruby_smb/fscc/file_information.rb +41 -8
  27. data/lib/ruby_smb/fscc/file_system_information/file_fs_attribute_information.rb +1 -0
  28. data/lib/ruby_smb/fscc/file_system_information/file_fs_volume_information.rb +1 -0
  29. data/lib/ruby_smb/fscc/file_system_information.rb +4 -0
  30. data/lib/ruby_smb/gss/provider/ntlm.rb +20 -3
  31. data/lib/ruby_smb/gss/provider.rb +10 -1
  32. data/lib/ruby_smb/ntlm/client.rb +74 -0
  33. data/lib/ruby_smb/ntlm.rb +1 -0
  34. data/lib/ruby_smb/server/cli.rb +121 -0
  35. data/lib/ruby_smb/server/server_client/session_setup.rb +4 -2
  36. data/lib/ruby_smb/server/server_client.rb +9 -1
  37. data/lib/ruby_smb/server/session.rb +5 -1
  38. data/lib/ruby_smb/server/share/provider/disk/processor/close.rb +9 -5
  39. data/lib/ruby_smb/server/share/provider/disk/processor/create.rb +2 -2
  40. data/lib/ruby_smb/server/share/provider/disk/processor/query.rb +2 -2
  41. data/lib/ruby_smb/server/share/provider/disk/processor/read.rb +15 -14
  42. data/lib/ruby_smb/server/share/provider/disk/processor.rb +76 -12
  43. data/lib/ruby_smb/server/share/provider/disk.rb +8 -2
  44. data/lib/ruby_smb/server/share/provider/virtual_disk/virtual_file.rb +85 -0
  45. data/lib/ruby_smb/server/share/provider/virtual_disk/virtual_pathname.rb +196 -0
  46. data/lib/ruby_smb/server/share/provider/virtual_disk/virtual_stat.rb +175 -0
  47. data/lib/ruby_smb/server/share/provider/virtual_disk.rb +116 -0
  48. data/lib/ruby_smb/server/share/provider.rb +1 -0
  49. data/lib/ruby_smb/server.rb +14 -3
  50. data/lib/ruby_smb/smb2/tree.rb +1 -0
  51. data/lib/ruby_smb/version.rb +1 -1
  52. data/spec/lib/ruby_smb/client_spec.rb +11 -2
  53. data/spec/lib/ruby_smb/fscc/file_information/file_access_information_spec.rb +21 -0
  54. data/spec/lib/ruby_smb/fscc/file_information/file_alignment_information_spec.rb +21 -0
  55. data/spec/lib/ruby_smb/fscc/file_information/file_all_information_spec.rb +61 -0
  56. data/spec/lib/ruby_smb/fscc/file_information/file_basic_information_spec.rb +41 -0
  57. data/spec/lib/ruby_smb/fscc/file_information/file_both_directory_information_spec.rb +59 -10
  58. data/spec/lib/ruby_smb/fscc/file_information/file_directory_information_spec.rb +30 -12
  59. data/spec/lib/ruby_smb/fscc/file_information/file_ea_information_spec.rb +21 -0
  60. data/spec/lib/ruby_smb/fscc/file_information/file_full_directory_information_spec.rb +30 -12
  61. data/spec/lib/ruby_smb/fscc/file_information/file_id_both_directory_information_spec.rb +63 -10
  62. data/spec/lib/ruby_smb/fscc/file_information/file_id_full_directory_information_spec.rb +30 -12
  63. data/spec/lib/ruby_smb/fscc/file_information/file_internal_information_spec.rb +21 -0
  64. data/spec/lib/ruby_smb/fscc/file_information/file_mode_information_spec.rb +21 -0
  65. data/spec/lib/ruby_smb/fscc/file_information/file_name_information_spec.rb +44 -0
  66. data/spec/lib/ruby_smb/fscc/file_information/file_names_information_spec.rb +30 -12
  67. data/spec/lib/ruby_smb/fscc/file_information/file_network_open_information_spec.rb +51 -0
  68. data/spec/lib/ruby_smb/fscc/file_information/file_normalized_name_information_spec.rb +44 -0
  69. data/spec/lib/ruby_smb/fscc/file_information/file_position_information_spec.rb +21 -0
  70. data/spec/lib/ruby_smb/fscc/file_information/file_rename_information_spec.rb +1 -1
  71. data/spec/lib/ruby_smb/fscc/file_information/file_standard_information_spec.rb +41 -0
  72. data/spec/lib/ruby_smb/fscc/file_information/file_stream_information_spec.rb +51 -0
  73. data/spec/lib/ruby_smb/fscc/file_information_spec.rb +14 -0
  74. data/spec/lib/ruby_smb/fscc/file_system_information/file_fs_attribute_information_spec.rb +46 -0
  75. data/spec/lib/ruby_smb/fscc/file_system_information/file_fs_volume_information_spec.rb +51 -0
  76. data/spec/lib/ruby_smb/fscc/file_system_information_spec.rb +14 -0
  77. data/spec/lib/ruby_smb/ntlm/client/session_spec.rb +114 -0
  78. data/spec/lib/ruby_smb/ntlm/client_spec.rb +36 -0
  79. data/spec/lib/ruby_smb/server/server_client_spec.rb +15 -0
  80. data/spec/lib/ruby_smb/server/share/provider/virtual_disk/virtual_pathname_spec.rb +581 -0
  81. data/spec/lib/ruby_smb/server/share/provider/virtual_disk/virtual_stat_spec.rb +207 -0
  82. data/spec/lib/ruby_smb/server/share/provider/virtual_disk_spec.rb +122 -0
  83. data.tar.gz.sig +2 -2
  84. metadata +63 -2
  85. metadata.gz.sig +0 -0
@@ -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'
@@ -6,6 +6,7 @@ module RubySMB
6
6
  # Currently, the server only supports negotiating and authenticating requests. No other server functionality is
7
7
  # available at this time. The negotiating and authentication is supported for SMB versions 1 through 3.1.1.
8
8
  class Server
9
+ require 'ruby_smb/server/cli'
9
10
  require 'ruby_smb/server/server_client'
10
11
  require 'ruby_smb/server/session'
11
12
  require 'ruby_smb/server/share'
@@ -14,8 +15,10 @@ module RubySMB
14
15
  Connection = Struct.new(:client, :thread)
15
16
 
16
17
  # @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)
18
+ # @param [Gss::Provider] gss_provider the authentication provider
19
+ # @param [::Logger] logger the logger to use for diagnostic messages
20
+ # @param thread_factory a block to create threads for serving clients
21
+ def initialize(server_sock: nil, gss_provider: nil, logger: nil, thread_factory: nil)
19
22
  server_sock = ::TCPServer.new(445) if server_sock.nil?
20
23
 
21
24
  @guid = Random.new.bytes(16)
@@ -36,6 +39,14 @@ module RubySMB
36
39
  @logger = logger
37
40
  end
38
41
 
42
+ if thread_factory.nil?
43
+ # the default thread factory uses Ruby's standard Thread#new
44
+ thread_factory = Proc.new do |_server_client, &block|
45
+ Thread.new(&block)
46
+ end
47
+ end
48
+ @thread_factory = thread_factory
49
+
39
50
  # share name => provider instance
40
51
  @shares = {
41
52
  'IPC$' => Share::Provider::IpcPipe.new
@@ -53,7 +64,7 @@ module RubySMB
53
64
  loop do
54
65
  sock = @socket.accept
55
66
  server_client = ServerClient.new(self, RubySMB::Dispatcher::Socket.new(sock, read_timeout: nil))
56
- @connections << Connection.new(server_client, Thread.new { server_client.run })
67
+ @connections << Connection.new(server_client, @thread_factory.call(server_client) { server_client.run })
57
68
 
58
69
  break unless block.nil? || block.call(server_client)
59
70
  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.0'.freeze
2
+ VERSION = '3.1.3'.freeze
3
3
  end
@@ -1279,6 +1279,9 @@ RSpec.describe RubySMB::Client do
1279
1279
  end
1280
1280
 
1281
1281
  it 'calls the backing methods' do
1282
+ request_packet = double('Request packet')
1283
+ allow(client).to receive(:negotiate_request).and_return(request_packet)
1284
+ allow(request_packet).to receive(:packet_smb_version)
1282
1285
  expect(client).to receive(:negotiate_request)
1283
1286
  expect(client).to receive(:send_recv)
1284
1287
  expect(client).to receive(:negotiate_response)
@@ -1319,14 +1322,20 @@ RSpec.describe RubySMB::Client do
1319
1322
 
1320
1323
  it 'increments the message ID' do
1321
1324
  expect(client).to receive(:smb2_message_id=).with(1)
1325
+ expect(client).to receive(:negotiate_request).twice.and_call_original
1326
+ expect(client).to receive(:parse_negotiate_response).twice do
1327
+ client.smb1 = false
1328
+ end
1322
1329
  client.negotiate
1323
1330
  end
1324
1331
 
1325
1332
  it 're-negotiates' do
1326
- expect(client).to receive(:negotiate_request).twice
1333
+ expect(client).to receive(:negotiate_request).twice.and_call_original
1327
1334
  expect(client).to receive(:send_recv).twice
1328
1335
  expect(client).to receive(:negotiate_response).twice
1329
- expect(client).to receive(:parse_negotiate_response).twice
1336
+ expect(client).to receive(:parse_negotiate_response).twice do
1337
+ client.smb1 = false
1338
+ end
1330
1339
  client.negotiate
1331
1340
  end
1332
1341
  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
@@ -27,6 +27,14 @@ RSpec.describe RubySMB::Fscc::FileInformation::FileBothDirectoryInformation do
27
27
  expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
28
28
  end
29
29
 
30
+ it 'tracks the next offset in a Uint32 field' do
31
+ expect(struct.next_offset).to be_a BinData::Uint32le
32
+ end
33
+
34
+ it 'tracks the file index in a Uint32 field' do
35
+ expect(struct.file_index).to be_a BinData::Uint32le
36
+ end
37
+
30
38
  it 'tracks the creation time in a Filetime field' do
31
39
  expect(struct.create_time).to be_a RubySMB::Field::FileTime
32
40
  end
@@ -43,6 +51,14 @@ RSpec.describe RubySMB::Fscc::FileInformation::FileBothDirectoryInformation do
43
51
  expect(struct.last_change).to be_a RubySMB::Field::FileTime
44
52
  end
45
53
 
54
+ it 'tracks the file size in a Int64 field' do
55
+ expect(struct.end_of_file).to be_a BinData::Int64le
56
+ end
57
+
58
+ it 'tracks the allocation size in a Int64 field' do
59
+ expect(struct.allocation_size).to be_a BinData::Int64le
60
+ end
61
+
46
62
  it 'contains the file attributes of the file' do
47
63
  expect(struct.file_attributes).to be_a RubySMB::Fscc::FileAttributes
48
64
  end
@@ -52,20 +68,53 @@ RSpec.describe RubySMB::Fscc::FileInformation::FileBothDirectoryInformation do
52
68
  expect(struct.file_name_length).to eq struct.file_name.do_num_bytes
53
69
  end
54
70
 
55
- it 'automatically encodes the file name in UTF-16LE' do
56
- name = 'Hello_world.txt'
57
- struct.file_name = name
58
- expect(struct.file_name.force_encoding('utf-16le')).to eq name.encode('utf-16le')
71
+ it 'tracks the extended attributes size in a Uint32 field' do
72
+ expect(struct.ea_size).to be_a BinData::Uint32le
59
73
  end
60
74
 
61
- describe 'reading in from a blob' do
62
- it 'uses the file_name_length to know when to stop reading' do
75
+ it 'tracks the short name length in a Uint8 field' do
76
+ expect(struct.short_name_length).to be_a BinData::Uint8
77
+ end
78
+
79
+ it 'tracks the short name in a String16 field' do
80
+ expect(struct.short_name).to be_a RubySMB::Field::String16
81
+ end
82
+
83
+ it 'tracks the file name in a String16 field' do
84
+ expect(struct.file_name).to be_a RubySMB::Field::String16
85
+ end
86
+
87
+ describe '#short_name' do
88
+ it 'automatically encodes the short name in UTF-16LE' do
89
+ name = 'Hello_world.'
90
+ struct.short_name = name
91
+ expect(struct.short_name.force_encoding('utf-16le')).to eq name.encode('utf-16le')
92
+ end
93
+
94
+ it 'is always exactly 24 bytes in length' do
95
+ struct.short_name = ''
96
+ expect(struct.short_name.num_bytes).to eq 24
97
+ struct.short_name = 'Hello_world.'
98
+ expect(struct.short_name.num_bytes).to eq 24
99
+ end
100
+ end
101
+
102
+ describe '#file_name' do
103
+ it 'automatically encodes the file name in UTF-16LE' do
63
104
  name = 'Hello_world.txt'
64
105
  struct.file_name = name
65
- blob = struct.to_binary_s
66
- blob << 'AAAA'
67
- new_from_blob = described_class.read(blob)
68
- expect(new_from_blob.file_name.force_encoding('utf-16le')).to eq name.encode('utf-16le')
106
+ expect(struct.file_name.force_encoding('utf-16le')).to eq name.encode('utf-16le')
107
+ end
108
+
109
+ describe 'reading in from a blob' do
110
+ it 'uses the file_name_length to know when to stop reading' do
111
+ name = 'Hello_world.txt'
112
+ struct.file_name = name
113
+ blob = struct.to_binary_s
114
+ blob << 'AAAA'
115
+ new_from_blob = described_class.read(blob)
116
+ expect(new_from_blob.file_name.force_encoding('utf-16le')).to eq name.encode('utf-16le')
117
+ end
69
118
  end
70
119
  end
71
120
  end
@@ -24,6 +24,14 @@ RSpec.describe RubySMB::Fscc::FileInformation::FileDirectoryInformation do
24
24
  expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
25
25
  end
26
26
 
27
+ it 'tracks the next offset in a Uint32 field' do
28
+ expect(struct.next_offset).to be_a BinData::Uint32le
29
+ end
30
+
31
+ it 'tracks the file index in a Uint32 field' do
32
+ expect(struct.file_index).to be_a BinData::Uint32le
33
+ end
34
+
27
35
  it 'tracks the creation time in a Filetime field' do
28
36
  expect(struct.create_time).to be_a RubySMB::Field::FileTime
29
37
  end
@@ -40,6 +48,14 @@ RSpec.describe RubySMB::Fscc::FileInformation::FileDirectoryInformation do
40
48
  expect(struct.last_change).to be_a RubySMB::Field::FileTime
41
49
  end
42
50
 
51
+ it 'tracks the file size in a Int64 field' do
52
+ expect(struct.end_of_file).to be_a BinData::Int64le
53
+ end
54
+
55
+ it 'tracks the allocation size in a Int64 field' do
56
+ expect(struct.allocation_size).to be_a BinData::Int64le
57
+ end
58
+
43
59
  it 'contains the file attributes of the file' do
44
60
  expect(struct.file_attributes).to be_a RubySMB::Fscc::FileAttributes
45
61
  end
@@ -49,20 +65,22 @@ RSpec.describe RubySMB::Fscc::FileInformation::FileDirectoryInformation do
49
65
  expect(struct.file_name_length).to eq struct.file_name.do_num_bytes
50
66
  end
51
67
 
52
- it 'automatically encodes the file name in UTF-16LE' do
53
- name = 'Hello_world.txt'
54
- struct.file_name = name
55
- expect(struct.file_name.force_encoding('utf-16le')).to eq name.encode('utf-16le')
56
- end
57
-
58
- describe 'reading in from a blob' do
59
- it 'uses the file_name_length to know when to stop reading' do
68
+ describe '#file_name' do
69
+ it 'automatically encodes the file name in UTF-16LE' do
60
70
  name = 'Hello_world.txt'
61
71
  struct.file_name = name
62
- blob = struct.to_binary_s
63
- blob << 'AAAA'
64
- new_from_blob = described_class.read(blob)
65
- expect(new_from_blob.file_name.force_encoding('utf-16le')).to eq name.encode('utf-16le')
72
+ expect(struct.file_name.force_encoding('utf-16le')).to eq name.encode('utf-16le')
73
+ end
74
+
75
+ describe 'reading in from a blob' do
76
+ it 'uses the file_name_length to know when to stop reading' do
77
+ name = 'Hello_world.txt'
78
+ struct.file_name = name
79
+ blob = struct.to_binary_s
80
+ blob << 'AAAA'
81
+ new_from_blob = described_class.read(blob)
82
+ expect(new_from_blob.file_name.force_encoding('utf-16le')).to eq name.encode('utf-16le')
83
+ end
66
84
  end
67
85
  end
68
86
  end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RubySMB::Fscc::FileInformation::FileEaInformation 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_EA_INFORMATION
7
+ end
8
+
9
+ subject(:struct) { described_class.new }
10
+
11
+ it { should respond_to :ea_size }
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 extended attributes size in a Uint32 field' do
18
+ expect(struct.ea_size).to be_a BinData::Uint32le
19
+ end
20
+
21
+ end
@@ -25,6 +25,14 @@ RSpec.describe RubySMB::Fscc::FileInformation::FileFullDirectoryInformation do
25
25
  expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
26
26
  end
27
27
 
28
+ it 'tracks the next offset in a Uint32 field' do
29
+ expect(struct.next_offset).to be_a BinData::Uint32le
30
+ end
31
+
32
+ it 'tracks the file index in a Uint32 field' do
33
+ expect(struct.file_index).to be_a BinData::Uint32le
34
+ end
35
+
28
36
  it 'tracks the creation time in a Filetime field' do
29
37
  expect(struct.create_time).to be_a RubySMB::Field::FileTime
30
38
  end
@@ -41,6 +49,14 @@ RSpec.describe RubySMB::Fscc::FileInformation::FileFullDirectoryInformation do
41
49
  expect(struct.last_change).to be_a RubySMB::Field::FileTime
42
50
  end
43
51
 
52
+ it 'tracks the file size in a Int64 field' do
53
+ expect(struct.end_of_file).to be_a BinData::Int64le
54
+ end
55
+
56
+ it 'tracks the allocation size in a Int64 field' do
57
+ expect(struct.allocation_size).to be_a BinData::Int64le
58
+ end
59
+
44
60
  it 'contains the file attributes of the file' do
45
61
  expect(struct.file_attributes).to be_a RubySMB::Fscc::FileAttributes
46
62
  end
@@ -50,20 +66,22 @@ RSpec.describe RubySMB::Fscc::FileInformation::FileFullDirectoryInformation do
50
66
  expect(struct.file_name_length).to eq struct.file_name.do_num_bytes
51
67
  end
52
68
 
53
- it 'automatically encodes the file name in UTF-16LE' do
54
- name = 'Hello_world.txt'
55
- struct.file_name = name
56
- expect(struct.file_name.force_encoding('utf-16le')).to eq name.encode('utf-16le')
57
- end
58
-
59
- describe 'reading in from a blob' do
60
- it 'uses the file_name_length to know when to stop reading' do
69
+ describe '#file_name' do
70
+ it 'automatically encodes the file name in UTF-16LE' do
61
71
  name = 'Hello_world.txt'
62
72
  struct.file_name = name
63
- blob = struct.to_binary_s
64
- blob << 'AAAA'
65
- new_from_blob = described_class.read(blob)
66
- expect(new_from_blob.file_name.force_encoding('utf-16le')).to eq name.encode('utf-16le')
73
+ expect(struct.file_name.force_encoding('utf-16le')).to eq name.encode('utf-16le')
74
+ end
75
+
76
+ describe 'reading in from a blob' do
77
+ it 'uses the file_name_length to know when to stop reading' do
78
+ name = 'Hello_world.txt'
79
+ struct.file_name = name
80
+ blob = struct.to_binary_s
81
+ blob << 'AAAA'
82
+ new_from_blob = described_class.read(blob)
83
+ expect(new_from_blob.file_name.force_encoding('utf-16le')).to eq name.encode('utf-16le')
84
+ end
67
85
  end
68
86
  end
69
87
  end