Class: RubySMB::SMB2::Tree

Inherits:
Object
  • Object
show all
Defined in:
lib/ruby_smb/smb2/tree.rb

Overview

An SMB2 connected remote Tree, as returned by a [RubySMB::SMB2::Packet::TreeConnectRequest]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(client:, share:, response:) ⇒ Tree

Returns a new instance of Tree.



26
27
28
29
30
31
32
# File 'lib/ruby_smb/smb2/tree.rb', line 26

def initialize(client:, share:, response:)
  @client             = client
  @share              = share
  @id                 = response.smb2_header.tree_id
  @permissions        = response.maximal_access
  @share_type         = response.share_type
end

Instance Attribute Details

#client ⇒ RubySMB::Client

Returns:



9
10
11
# File 'lib/ruby_smb/smb2/tree.rb', line 9

def client
  @client
end

#id ⇒ Integer

Returns:

  • (Integer)


24
25
26
# File 'lib/ruby_smb/smb2/tree.rb', line 24

def id
  @id
end

#permissions ⇒ RubySMB::SMB2::BitField::DirectoryAccessMask



14
15
16
# File 'lib/ruby_smb/smb2/tree.rb', line 14

def permissions
  @permissions
end

#share ⇒ String

Returns:

  • (String)


19
20
21
# File 'lib/ruby_smb/smb2/tree.rb', line 19

def share
  @share
end

Instance Method Details

#disconnect! ⇒ WindowsError::ErrorCode

Disconnects this Tree from the current session

Returns:

  • (WindowsError::ErrorCode) —

    the NTStatus sent back by the server.



37
38
39
40
41
42
43
# File 'lib/ruby_smb/smb2/tree.rb', line 37

def disconnect!
  request = RubySMB::SMB2::Packet::TreeDisconnectRequest.new
  request = set_header_fields(request)
  raw_response = client.send_recv(request)
  response = RubySMB::SMB2::Packet::TreeDisconnectResponse.read(raw_response)
  response.status_code
end

#list(directory: nil, pattern: '*', type: RubySMB::Fscc::FileInformation::FileIdFullDirectoryInformation) ⇒ Array

List directory on the remote share.

Examples:

tree = client.tree_connect("\\\\192.168.99.134\\Share")
tree.list(directory: "path\\to\\directory")

Parameters:

  • directory (String) (defaults to: nil) —

    path to the directory to be listed

  • pattern (String) (defaults to: '*') —

    search pattern

  • type (Class) (defaults to: RubySMB::Fscc::FileInformation::FileIdFullDirectoryInformation) —

    file information class

Returns:

  • (Array) —

    array of directory structures



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/ruby_smb/smb2/tree.rb', line 119

def list(directory: nil, pattern: '*', type: RubySMB::Fscc::FileInformation::FileIdFullDirectoryInformation)
  create_response = open_directory(directory: directory)
  file_id         = create_response.file_id

  directory_request                         = RubySMB::SMB2::Packet::QueryDirectoryRequest.new
  directory_request.file_information_class  = type::CLASS_LEVEL
  directory_request.file_id                 = file_id
  directory_request.name                    = pattern
  directory_request.output_length           = 65_535

  directory_request = set_header_fields(directory_request)

  files = []

  loop do
    response            = client.send_recv(directory_request)
    directory_response  = RubySMB::SMB2::Packet::QueryDirectoryResponse.read(response)

    status_code         = directory_response.smb2_header.nt_status.to_nt_status

    break if status_code == WindowsError::NTStatus::STATUS_NO_MORE_FILES

    unless status_code == WindowsError::NTStatus::STATUS_SUCCESS

      raise RubySMB::Error::UnexpectedStatusCode, status_code.to_s
    end

    files += directory_response.results(type)
    # Reset the message id so the client can update appropriately.
    directory_request.smb2_header.message_id = 0
  end

  files
end

#open_directory(directory: nil, disposition: RubySMB::Dispositions::FILE_OPEN, impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE, read: true, write: false, delete: false) ⇒ RubySMB::SMB2::Packet::CreateResponse

'Opens' a directory file on the remote end, using a CreateRequest. This can be used to open an existing directory, or create a new one, depending on the disposition set.

Parameters:

  • directory (String) (defaults to: nil) —

    the name of the directory file

  • disposition (Integer) (defaults to: RubySMB::Dispositions::FILE_OPEN) —

    the create disposition to use, should be one of Dispositions

  • impersonation (Integer) (defaults to: RubySMB::ImpersonationLevels::SEC_IMPERSONATE) —

    the impersonation level to use, should be one of ImpersonationLevels

  • read (Boolean) (defaults to: true) —

    whether to request read access

  • write (Boolean) (defaults to: false) —

    whether to request write access

  • delete (Boolean) (defaults to: false) —

    whether to request delete access

Returns:



165
166
167
168
169
170
171
172
173
# File 'lib/ruby_smb/smb2/tree.rb', line 165

def open_directory(directory: nil, disposition: RubySMB::Dispositions::FILE_OPEN,
                   impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE,
                   read: true, write: false, delete: false)

  create_request  = open_directory_packet(directory: directory, disposition: disposition,
                                          impersonation: impersonation, read: read, write: write, delete: delete)
  raw_response    = client.send_recv(create_request)
  RubySMB::SMB2::Packet::CreateResponse.read(raw_response)
end

#open_directory_packet(directory: nil, disposition: RubySMB::Dispositions::FILE_OPEN, impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE, read: true, write: false, delete: false) ⇒ RubySMB::SMB2::Packet::CreateRequest

Creates the Packet for the #open_directory method.

Parameters:

  • directory (String) (defaults to: nil) —

    the name of the directory file

  • disposition (Integer) (defaults to: RubySMB::Dispositions::FILE_OPEN) —

    the create disposition to use, should be one of Dispositions

  • impersonation (Integer) (defaults to: RubySMB::ImpersonationLevels::SEC_IMPERSONATE) —

    the impersonation level to use, should be one of ImpersonationLevels

  • read (Boolean) (defaults to: true) —

    whether to request read access

  • write (Boolean) (defaults to: false) —

    whether to request write access

  • delete (Boolean) (defaults to: false) —

    whether to request delete access

Returns:



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/ruby_smb/smb2/tree.rb', line 184

def open_directory_packet(directory: nil, disposition: RubySMB::Dispositions::FILE_OPEN,
                          impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE,
                          read: true, write: false, delete: false)
  create_request = RubySMB::SMB2::Packet::CreateRequest.new
  create_request = set_header_fields(create_request)

  create_request.impersonation_level            = impersonation
  create_request.create_options.directory_file  = 1
  create_request.file_attributes.directory      = 1
  create_request.desired_access.list            = 1
  create_request.share_access.read_access       = 1 if read
  create_request.share_access.write_access      = 1 if write
  create_request.share_access.delete_access     = 1 if delete
  create_request.create_disposition             = disposition

  if directory.nil? || directory.empty?
    create_request.name = "\x00"
    create_request.name_length = 0
  else
    create_request.name = directory
  end
  create_request
end

#open_file(filename:, attributes: nil, options: nil, disposition: RubySMB::Dispositions::FILE_OPEN, impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE, read: true, write: false, delete: false) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/ruby_smb/smb2/tree.rb', line 45

def open_file(filename:, attributes: nil, options: nil, disposition: RubySMB::Dispositions::FILE_OPEN,
              impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE, read: true, write: false, delete: false)

  create_request = RubySMB::SMB2::Packet::CreateRequest.new
  create_request = set_header_fields(create_request)

  # If the user supplied file attributes, use those, otherwise set some
  # sane defaults.
  if attributes
    create_request.file_attributes = attributes
  else
    create_request.file_attributes.directory  = 0
    create_request.file_attributes.normal     = 1
  end

  # If the user supplied Create Options, use those, otherwise set some
  # sane defaults.
  if options
    create_request.create_options = options
  else
    create_request.create_options.directory_file      = 0
    create_request.create_options.non_directory_file  = 1
  end

  if read
    create_request.share_access.read_access = 1
    create_request.desired_access.read_data = 1
  end

  if write
    create_request.share_access.write_access   = 1
    create_request.desired_access.write_data   = 1
    create_request.desired_access.append_data  = 1
  end

  if delete
    create_request.share_access.delete_access   = 1
    create_request.desired_access.delete_access = 1
  end

  create_request.requested_oplock     = 0xff
  create_request.impersonation_level  = impersonation
  create_request.create_disposition   = disposition
  create_request.name                 = filename

  raw_response  = client.send_recv(create_request)
  response      = RubySMB::SMB2::Packet::CreateResponse.read(raw_response)

  if response.is_a?(RubySMB::SMB2::Packet::ErrorPacket)
    raise RubySMB::Error::RubySMBError
  end

  case @share_type
  when 0x01
    RubySMB::SMB2::File.new(name: filename, tree: self, response: response)
  when 0x02
    RubySMB::SMB2::Pipe.new(name: filename, tree: self, response: response)
  # when 0x03
  #   it's a printer!
  else
    raise RubySMB::Error::RubySMBError
  end
end

#set_header_fields(request) ⇒ RubySMB::SMB2::Packet

Sets a few preset header fields that will always be set the same way for Tree operations. This is, the TreeID, Credits, and Credit Charge.

Parameters:

Returns:



213
214
215
216
217
218
# File 'lib/ruby_smb/smb2/tree.rb', line 213

def set_header_fields(request)
  request.smb2_header.tree_id = id
  request.smb2_header.credit_charge = 1
  request.smb2_header.credits = 256
  request
end