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.
Default system contents(4) path.
Install class to use if not specified.
Regular expression for filetype.
Regular expression for group name.
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”).
Regular expression for major device mode.
Regular expression for minor device mode.
Regular expression for octal file mode.
Regular expression for file modification time.
Regular expression for user name.
Regular expression for a package.
Regular expression for path.
Regular expression for file size.
Regular expression for file sum.
The file type of the contents entry: one of the symbols :b, :c, :d, :e, :f, :v, :x, :l, :s.
The file’s group (string).
The installation class of the file.
The major mode (integer), for device files (ftype :b or :c).
The minor mode (integer), for device files (ftype :b or :c).
The mode of the file, as an integer. This is usually presented as an octal number.
The file modification time (seconds from the start of 1970).
The file user (string).
Array of package names to which this file belongs.
The path of the file.
The relative path for a linked file.
The size of the file in bytes.
The size of the file in bytes modulo 65535. (See #sum).
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
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
Create a new contents(4) object.
# File lib/solaris/contents.rb, line 214 def initialize @packages = [] end
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
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
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
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
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
Convert the file mode to a 4-digit octal string.
# File lib/solaris/contents.rb, line 261 def mode_s '%04o' % @mode end