class Solaris::Contents

Class to represent Solaris package file contents. A contents line contains information regarding file type, location, ownership, permisions.

See Solaris’ contents(4) man-page for information on attributes.

Constants

DEFAULT_CONTENTS_PATH

Default system contents(4) path.

DEFAULT_INSTALL_CLASS

Install class to use if not specified.

RE_FTYPE

Regular expression for filetype.

RE_GROUP

Regular expression for group name.

RE_INSTALL_CLASS

Regular expression for install class. Solaris documentation states that this parameter is at most 12 characters but there are counterexamples in the wild from Sun/Oracle’s own hand (eg class “pkcs11confbase”).

RE_MAJOR

Regular expression for major device mode.

RE_MINOR

Regular expression for minor device mode.

RE_MODE

Regular expression for octal file mode.

RE_MTIME

Regular expression for file modification time.

RE_OWNER

Regular expression for user name.

RE_PACKAGE

Regular expression for a package.

RE_PATH

Regular expression for path.

RE_SIZE

Regular expression for file size.

RE_SUM

Regular expression for file sum.

Attributes

ftype[RW]

The file type of the contents entry: one of the symbols :b, :c, :d, :e, :f, :v, :x, :l, :s.

group[RW]

The file’s group (string).

install_class[RW]

The installation class of the file.

major[RW]

The major mode (integer), for device files (ftype :b or :c).

minor[RW]

The minor mode (integer), for device files (ftype :b or :c).

mode[RW]

The mode of the file, as an integer. This is usually presented as an octal number.

mtime[RW]

The file modification time (seconds from the start of 1970).

owner[RW]

The file user (string).

packages[RW]

Array of package names to which this file belongs.

path[RW]

The path of the file.

rpath[RW]

The relative path for a linked file.

size[RW]

The size of the file in bytes.

sum[RW]

The size of the file in bytes modulo 65535. (See #sum).

Public Class Methods

from_line(line) click to toggle source

Create a Contents object from a line from a contents(4) file. If line is empty or a comment (starts with a hash character) then return nil.

# File lib/solaris/contents.rb, line 104
def self.from_line(line)
  return nil if line.empty? || line =~ %r^#/
  ftype = $2.to_sym if line =~ %r^#{RE_PATH} #{RE_FTYPE} /
  re = case ftype
       when :s, :l
         %r^#{RE_PATH}=#{RE_PATH} #{ftype} #{RE_INSTALL_CLASS} (#{RE_PACKAGE}( #{RE_PACKAGE})*)$/
       when :d
         %r^#{RE_PATH} #{ftype} #{RE_INSTALL_CLASS} #{RE_MODE} #{RE_OWNER} #{RE_GROUP} (#{RE_PACKAGE}( #{RE_PACKAGE})*)$/
       when :x
         %r^#{RE_PATH} #{ftype} #{RE_INSTALL_CLASS} #{RE_MODE} #{RE_OWNER} #{RE_GROUP} #{RE_PACKAGE}$/
       when :b, :c
         %r^#{RE_PATH} #{ftype} #{RE_INSTALL_CLASS} #{RE_MAJOR} #{RE_MINOR} #{RE_MODE} #{RE_OWNER} #{RE_GROUP} #{RE_PACKAGE}$/
       when :f, :v, :e
         %r^#{RE_PATH} #{ftype} #{RE_INSTALL_CLASS} #{RE_MODE} #{RE_OWNER} #{RE_GROUP} #{RE_SIZE} #{RE_SUM} #{RE_MTIME} (#{RE_PACKAGE}( #{RE_PACKAGE})*)$/
       else
         raise ArgumentError, "Unknown filetype in line #{line.inspect}"
       end
  if line =~ re
    contents = self.new
    contents.ftype = ftype
    contents.path = $1
    case ftype
    when :s, :l
      contents.rpath = $2
      contents.install_class = $3
      contents.packages = $4.split( %r\s+/ ).map { |pkg| Pkg.new(pkg) }
    when :d, :x
      contents.install_class = $2
      contents.mode = $3.to_i( 8 )
      contents.owner = $4
      contents.group = $5
      contents.packages = $6.split( %r\s+/ ).map { |pkg| Pkg.new(pkg) }
    when :b, :c
      contents.install_class = $2
      contents.major = $3.to_i
      contents.minor = $4.to_i
      contents.mode = $5.to_i( 8 )
      contents.owner = $6
      contents.group = $7
      contents.packages = $8.split( %r\s+/ ).map { |pkg| Pkg.new(pkg) }
    when :f, :v, :e
      contents.install_class = $2
      contents.mode = $3.to_i( 8 )
      contents.owner = $4
      contents.group =$5
      contents.size = $6.to_i
      contents.sum = $7.to_i
      contents.mtime = $8.to_i
      contents.packages = $9.split( %r\s+/ ).map { |pkg| Pkg.new(pkg) }
    end
  else
    raise ArgumentError, "Could not parse line #{line.inspect}"
  end
  contents
end
from_path(path, actual=nil) click to toggle source

Create a Contents entry from the file at the path on the local filesystem.

If actual is provided then this is the path that is used for the object’s pathname property although all other properties are created from the path argument. The process must be able to stat(2) the file at path to determine these properties.

# File lib/solaris/contents.rb, line 167
def self.from_path(path, actual=nil)
  contents = self.new
  # Use #lstat since we are always interested in the link source,
  # not the target.
  stat = File.lstat( path )
  raise RuntimeError, 'Unknown file type' if stat.ftype == 'unknown'
  # Stat returns "link" for symlink, not "symlink"
  contents.ftype = stat.symlink? ? :s : stat.ftype[0].to_sym
  case contents.ftype
  when :f
    contents.sum = sum( path )
    contents.size = stat.size
    contents.mtime = stat.mtime.to_i
  when :s
    contents.rpath = File.realpath( path )
  when :b, :c
    contents.major = stat.dev_major
    contents.minor = stat.dev_minor
  when :d
    #
  else
    raise RuntimeError, "Unknown ftype #{contents.ftype.inspect}"
  end
  contents.path = actual || path
  contents.install_class = DEFAULT_INSTALL_CLASS
  contents.mode = stat.mode & 07777
  contents.owner = Etc.getpwuid( stat.uid ).name
  contents.group = Etc.getgrgid( stat.gid ).name
  contents
end
new() click to toggle source

Create a new contents(4) object.

# File lib/solaris/contents.rb, line 214
def initialize
  @packages = []
end
read(path=DEFAULT_CONTENTS_PATH) click to toggle source

Read a contents(4) file (default /var/sadm/install/contents) and return an array of package contents entries.

# File lib/solaris/contents.rb, line 200
def self.read(path=DEFAULT_CONTENTS_PATH)
  File.open( path ).lines.map do |line|
    from_line( line )
  end.compact
end
sum(io_or_string) click to toggle source

Return the sum of the byte values of the file, modulo 65535. This is the value returned by Solaris’ sum(1) (NB. not cksum(1) or sum(1B)). This is a weak checksum and should not be used for security purposes.

# File lib/solaris/contents.rb, line 209
def self.sum(io_or_string)
  io_or_string.each_byte.inject { |r, v| ( r + v ) & 0xffff }
end

Public Instance Methods

package() click to toggle source

Return nil if no package has been specified for this contents entry. If only one package has been specified for this contents entry (all cases except, possibly, directory) then return that package. Otherwise throw a RuntimeError.

# File lib/solaris/contents.rb, line 222
def package
  raise RuntimeError, 'Ambiguous: contains more than one package' if @packages.size > 1
  @packages[0]
end
to_s() click to toggle source

Convert the object to a contents(4) line (string).

# File lib/solaris/contents.rb, line 228
def to_s
  case @ftype
  when :b, :c
    [ @path, @ftype, @install_class, @major, @minor, mode_s, @owner, @group ] + @packages
  when :d
    [ @path, @ftype, @install_class, mode_s, @owner, @group ] + @packages
  when :x
    [ @path, @ftype, @install_class, mode_s, @owner, @group ] + @packages
  when :e, :f, :v
    [ @path, @ftype, @install_class, mode_s, @owner, @group, @size, @sum, @mtime ] + @packages
  when :l, :s
    [ "#{@path}=#{@rpath}", @ftype, @install_class ] + @packages
  else
    raise RuntimeError, "Unknown ftype #{@ftype.inspect}"
  end.join( ' ' )
end
valid?() click to toggle source

Returns true if the object is a valid contents specification, false otherwise.

# File lib/solaris/contents.rb, line 247
def valid?
  begin
    self.class.from_line( to_s )
  rescue ArgumentError, RuntimeError
    false
  else
    true
  end

end

Private Instance Methods

mode_s() click to toggle source

Convert the file mode to a 4-digit octal string.

# File lib/solaris/contents.rb, line 261
def mode_s
  '%04o' % @mode
end