Class: Homebrew::API::PackagesIndex Private

Inherits:
Object
  • Object
show all
Defined in:
api/packages_index.rb

Overview

This class is part of a private API. This class may only be used in the Homebrew/brew repository. Third parties should avoid using this class if possible, as it may be removed or changed without warning.

Byte-offset index into a signature-verified internal packages JWS payload, so commands can parse only the entries they need instead of the whole multi-megabyte document.

The index is derived, unverified cache data guarded in layers: the payload bytes it points into are signature-verified on every run, loading requires the recorded top-level spans to tile that payload exactly (so the formulae and casks section spans are provably the real top-level values) and every lookup revalidates that its offsets sit at the expected "<name>": key inside the requested section's span and that the slice parses. A forged or stale index therefore cannot inject unverified content or remap a name to another entry, even a matching key in the other section; it fails validation and callers fall back to a full parse when Invalid is raised.

Defined Under Namespace

Classes: Invalid

Constant Summary collapse

FORMAT_VERSION =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

1
SECTION_KEYS =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

%w[formulae casks].freeze
MAX_FALSE_MATCH_RETRIES =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

Bounds index building when payload bytes stop round-tripping through JSON.generate; giving up just means no index is written.

100

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(payload:, source_stat:, top_level:, sections:) ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:



203
204
205
206
207
208
# File 'api/packages_index.rb', line 203

def initialize(payload:, source_stat:, top_level:, sections:)
  @payload = payload
  @source_stat = source_stat
  @top_level = top_level
  @sections = sections
end

Instance Attribute Details

#payloadString (readonly)

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



194
195
196
# File 'api/packages_index.rb', line 194

def payload
  @payload
end

#source_statFile::Stat (readonly)

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:

  • (File::Stat)


197
198
199
# File 'api/packages_index.rb', line 197

def source_stat
  @source_stat
end

Class Method Details

.build(payload:, parsed:) ⇒ Hash{String => Hash{String => Array<(Integer, Integer)>}}?

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Locates every top-level value and every formula and cask entry in the payload bytes. Offsets are found by searching for each JSON key in document order and validating that the following bytes byte-match the entry's JSON.generate round trip, so every recorded offset provably reproduces the canonical parse.

Parameters:

Returns:



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'api/packages_index.rb', line 132

def self.build(payload:, parsed:)
  data = T.let({ "top_level" => {} }, T::Hash[String, T::Hash[String, [Integer, Integer]]])
  SECTION_KEYS.each { |section| data[section] = {} }
  retries = 0
  position = 0

  parsed.each do |key, value|
    location = locate(payload, key, value, position)
    return nil if location.nil?

    value_start, value_bytesize = location
    T.must(data["top_level"])[key] = [value_start, value_bytesize]

    if SECTION_KEYS.include?(key) && value.is_a?(Hash)
      entry_position = value_start
      value.each do |name, entry|
        entry_location = T.let(nil, T.nilable([Integer, Integer]))
        loop do
          entry_location = locate(payload, name, entry, entry_position)
          break unless entry_location.nil?

          retries += 1
          return nil if retries > MAX_FALSE_MATCH_RETRIES

          next_position = payload.byteindex("#{name.to_json}:", entry_position)
          return nil if next_position.nil?

          entry_position = next_position + 1
        end

        entry_start, entry_bytesize = entry_location
        T.must(data[key])[name] = [entry_start, entry_bytesize]
        entry_position = entry_start + entry_bytesize
      end
    end

    position = value_start + value_bytesize
  end

  data
end

.load(target, payload:, source_stat:) ⇒ PackagesIndex?

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'api/packages_index.rb', line 44

def self.load(target, payload:, source_stat:)
  data = JSON.parse(path_for(target).read(encoding: Encoding::UTF_8))
  return unless data.is_a?(Hash)
  return if data["version"] != FORMAT_VERSION
  return if source_fingerprint(source_stat).any? { |key, value| data[key] != value }
  return if data["payload_bytesize"] != payload.bytesize

  top_level = data["top_level"]
  sections = data.slice(*SECTION_KEYS)
  return unless top_level.is_a?(Hash)
  return unless sections.values.all?(Hash)
  return unless top_level_spans_tile_payload?(payload, top_level)

  new(payload:, source_stat:, top_level:, sections:)
rescue SystemCallError, JSON::ParserError
  nil
end

.path_for(target) ⇒ Pathname

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:



31
32
33
# File 'api/packages_index.rb', line 31

def self.path_for(target)
  Pathname("#{target}.payload.index")
end

.source_fingerprint(stat) ⇒ Hash{String => Integer}

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

  • stat (File::Stat)

Returns:



36
37
38
39
40
41
# File 'api/packages_index.rb', line 36

def self.source_fingerprint(stat)
  {
    "source_size"     => stat.size,
    "source_mtime_ns" => (stat.mtime.to_r * 1_000_000_000).to_i,
  }
end

.write!(target, payload:, parsed:, source_stat:) ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.

Builds and persists an index for a freshly verified and parsed payload. Failing to build or write one only costs the fast path.

Parameters:



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'api/packages_index.rb', line 100

def self.write!(target, payload:, parsed:, source_stat:)
  # Never write to a user-owned cache as root, matching `skip_download?`.
  return if Homebrew.running_as_root_but_not_owned_by_root?
  return if (data = build(payload:, parsed:)).nil?

  data = {
    "version"          => FORMAT_VERSION,
    **source_fingerprint(source_stat),
    "payload_bytesize" => payload.bytesize,
    **data,
  }
  index_path = path_for(target)
  temporary_path = Pathname("#{index_path}.tmp")
  begin
    temporary_path.write(JSON.generate(data))
    File.rename(temporary_path, index_path)
  ensure
    temporary_path.unlink if temporary_path.exist?
  end
rescue SystemCallError
  nil
end

Instance Method Details

#cask_hash(name) ⇒ Hash{String => T.untyped}?

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:



216
217
218
# File 'api/packages_index.rb', line 216

def cask_hash(name)
  entry_value("casks", name)
end

#cask_name?(name) ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:

  • (Boolean)


236
237
238
# File 'api/packages_index.rb', line 236

def cask_name?(name)
  @sections.fetch("casks", {}).key?(name)
end

#cask_namesArray<String>

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



226
227
228
# File 'api/packages_index.rb', line 226

def cask_names
  @sections.fetch("casks", {}).keys
end

#formula_hash(name) ⇒ Hash{String => T.untyped}?

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:



211
212
213
# File 'api/packages_index.rb', line 211

def formula_hash(name)
  entry_value("formulae", name)
end

#formula_name?(name) ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:

  • (Boolean)


231
232
233
# File 'api/packages_index.rb', line 231

def formula_name?(name)
  @sections.fetch("formulae", {}).key?(name)
end

#formula_namesArray<String>

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



221
222
223
# File 'api/packages_index.rb', line 221

def formula_names
  @sections.fetch("formulae", {}).keys
end

#top_level_value(key) ⇒ T.untyped

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:

  • (T.untyped)


241
242
243
244
245
246
247
248
# File 'api/packages_index.rb', line 241

def top_level_value(key)
  return if SECTION_KEYS.include?(key)

  location = @top_level[key]
  return if location.nil?

  slice_value(key, location)
end