Package org.redline_rpm.payload

Source Code of org.redline_rpm.payload.Contents$HeaderComparator

package org.redline_rpm.payload;

import org.redline_rpm.ChannelWrapper.Key;
import org.redline_rpm.ReadableChannelWrapper;
import org.redline_rpm.Util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Logger;

import static java.util.Arrays.asList;
import static java.util.logging.Level.FINE;
import static java.util.logging.Logger.getLogger;
import static org.redline_rpm.Util.normalizePath;
import static org.redline_rpm.payload.CpioHeader.DEFAULT_DIRECTORY_PERMISSION;
import static org.redline_rpm.payload.CpioHeader.DEFAULT_FILE_PERMISSION;
import static org.redline_rpm.payload.CpioHeader.DEFAULT_GROUP;
import static org.redline_rpm.payload.CpioHeader.DEFAULT_USERNAME;
import static org.redline_rpm.payload.CpioHeader.DIR;
import static org.redline_rpm.payload.CpioHeader.FILE;
import static org.redline_rpm.payload.CpioHeader.SYMLINK;

/**
* The contents of an RPM archive. These entries define the files and links that
* the RPM contains as well as headers those files require. Note that the RPM format
* requires that files in the archive be naturally ordered.
*/
public class Contents {

  private static final Set< String> BUILTIN = new HashSet< String>();
  private static final Set< String> DOC_DIRS = new HashSet< String>();
  static {
    BUILTIN.add( "/");
    BUILTIN.add( "/bin");
    BUILTIN.add( "/dev");
    BUILTIN.add( "/etc");
    BUILTIN.add( "/etc/bash_completion.d");
    BUILTIN.add( "/etc/cron.d");
    BUILTIN.add( "/etc/cron.daily");
    BUILTIN.add( "/etc/cron.hourly");
    BUILTIN.add( "/etc/cron.monthly");
    BUILTIN.add( "/etc/cron.weekly");
    BUILTIN.add( "/etc/default");
    BUILTIN.add( "/etc/init.d");
    BUILTIN.add( "/etc/logrotate.d");
    BUILTIN.add( "/lib");
    BUILTIN.add( "/usr");
    BUILTIN.add( "/usr/bin");
    BUILTIN.add( "/usr/lib");
    BUILTIN.add( "/usr/local");
    BUILTIN.add( "/usr/local/bin");
    BUILTIN.add( "/usr/local/lib");
    BUILTIN.add( "/usr/sbin");
    BUILTIN.add( "/usr/share");
    BUILTIN.add( "/usr/share/applications");
    BUILTIN.add( "/root");
    BUILTIN.add( "/sbin");
    BUILTIN.add( "/opt");
    BUILTIN.add( "/tmp");
    BUILTIN.add( "/var");
    BUILTIN.add( "/var/lib");
    BUILTIN.add( "/var/log");
    DOC_DIRS.add("/usr/doc");
    DOC_DIRS.add("/usr/man");
    DOC_DIRS.add("/usr/X11R6/man");
    DOC_DIRS.add("/usr/share/doc");
    DOC_DIRS.add("/usr/share/man");
    DOC_DIRS.add("/usr/share/info");
  }

  private Logger logger = getLogger( Contents.class.getName());
  private int inode = 1;

  protected final Set< CpioHeader> headers = new TreeSet< CpioHeader>( new HeaderComparator());
  protected final Set< String> files = new HashSet< String>();
  protected final Map< CpioHeader, Object> sources = new HashMap< CpioHeader, Object>();
  protected final Set< String> builtins = new HashSet< String>();
 
  public Contents()
  {
    builtins.addAll(BUILTIN);
  }

  /**
   * Adds a directory entry to the archive with the default permissions of 644.
   *
   * @param path the destination path for the installed file.
   * @param target the target string
   */
  public synchronized void addLink( final String path, final String target) {
    addLink( path, target, -1);
  }

  /**
   * Adds a directory entry to the archive with the specified permissions.
   *
   * @param path the destination path for the installed file.
   * @param target the target string
   * @param permissions the permissions flags.
   */
  public synchronized void addLink( String path, final String target, int permissions) {
    if ( files.contains( path)) return;
    files.add( path);
    logger.log( FINE, "Adding link ''{0}''.", path);
    CpioHeader header = new CpioHeader( path);
    header.setType( SYMLINK);
    header.setFileSize( target.length());
    header.setMtime( System.currentTimeMillis());
    if ( permissions != -1) header.setPermissions( permissions);
    headers.add( header);
    sources.put( header, target);
  }

  /**
   * Adds a directory entry to the archive with the default permissions of 644.
   *
   * @param path the destination path for the installed file.
   */
  public synchronized void addDirectory( final String path) {
    addDirectory( path, -1);
  }
 
  /**
   * Adds a directory entry to the archive with the default permissions of 644.
   *
   * @param path the destination path for the installed file.
   * @param directive directive indicating special handling for this directory.
   */
  public synchronized void addDirectory( final String path, final Directive directive) {
    addDirectory( path, -1, directive, null, null);
  }
 
  /**
   * Adds a directory entry to the archive with the specified permissions.
   *
   * @param path the destination path for the installed file.
   * @param permissions the permissions flags.
   */
  public synchronized void addDirectory( final String path, final int permissions) {
    addDirectory(path, permissions, null, null, null);
  }

  /**
   * Adds a directory entry to the archive with the specified permissions.
   *
   * @param path the destination path for the installed file.
   * @param permissions the permissions flags.
   * @param directive directive indicating special handling for this directory.
   * @param uname user owner for the given file
   * @param gname group owner for the given file
   */
  public synchronized void addDirectory( final String path, final int permissions, final Directive directive, final String uname, final String gname) {
    addDirectory(path, permissions, directive, uname, gname, true);
  }

  /**
   * Adds a directory entry to the archive with the specified permissions.
   *
   * @param path the destination path for the installed file.
   * @param permissions the permissions flags.
   * @param directive directive indicating special handling for this directory.
   * @param uname user owner for the given file
   * @param gname group owner for the given file
   * @param addParents whether to add parent directories to the rpm
   */
  public synchronized void addDirectory( final String path, final int permissions, final Directive directive, final String uname, final String gname, boolean addParents) {
    if ( files.contains( path)) return;

    if ( addParents) addParents( new File( path), permissions, uname, gname);
    files.add( path);
    logger.log( FINE, "Adding directory ''{0}''.", path);
    CpioHeader header = new CpioHeader( path);
    header.setType( DIR);
    header.setInode( inode++);
    if ( null == uname) {
      header.setUname(DEFAULT_USERNAME);
    } else if (0 == uname.length()) {
      header.setUname(DEFAULT_USERNAME);
    } else {
      header.setUname(uname);
    }
    if ( null == gname) {
      header.setGname(DEFAULT_GROUP);
    } else if (0 == gname.length()) {
      header.setGname(DEFAULT_GROUP);
    } else {
      header.setGname(gname);
    }
    header.setMtime( System.currentTimeMillis());
    if ( -1 == permissions) {
      header.setPermissions( DEFAULT_DIRECTORY_PERMISSION);
    } else {
      header.setPermissions( permissions);
    }
    headers.add( header);
    sources.put( header, "");
    if ( directive != null) header.setFlags( directive.flag());
  }

  /**
   * Adds a file entry to the archive with the default permissions of 644.
   *
   * @param path the destination path for the installed file.
   * @param source the local file to be included in the package.
   * @throws java.io.FileNotFoundException file wasn't found
   */
  public void addFile( final String path, final File source) throws FileNotFoundException {
    addFile( path, source, -1);
  }

  /**
   * Adds a file entry to the archive with the specified permissions.
   *
   * @param path the destination path for the installed file.
   * @param source the local file to be included in the package.
   * @param permissions the permissions flags.
   * @throws java.io.FileNotFoundException file wasn't found
   */
  public void addFile( final String path, final File source, int permissions) throws FileNotFoundException {
    addFile(path, source, permissions, null, null, null);
  }

  /**
   * Adds a file entry to the archive with the specified permissions.
   *
   * @param path the destination path for the installed file.
   * @param source the local file to be included in the package.
   * @param permissions the permissions flags.
   * @param dirmode permission flags for parent directories, use -1 to leave as default.
   * @throws java.io.FileNotFoundException file wasn't found
   */
  public void addFile( final String path, final File source, int permissions, int dirmode) throws FileNotFoundException {
    addFile(path, source, permissions, null, null, null, dirmode);
  }
 
  /**
   * Adds a file entry to the archive with the specified permissions.
   *
   * @param path the destination path for the installed file.
   * @param source the local file to be included in the package.
   * @param permissions the permissions flags.
   * @param directive directive indicating special handling for this file.
   * @throws java.io.FileNotFoundException file wasn't found
   */
  public void addFile( final String path, final File source, int permissions, final Directive directive) throws FileNotFoundException {
    addFile(path, source, permissions, directive, null, null);
  }

  /**
   * Adds a file entry to the archive with the specified permissions.
   *
   * @param path the destination path for the installed file.
   * @param source the local file to be included in the package.
   * @param permissions the permissions flags.
   * @param directive directive indicating special handling for this file.
   * @param uname user owner for the given file
   * @param gname group owner for the given file
   * @throws java.io.FileNotFoundException file wasn't found
   */
  public void addFile( final String path, final File source, final int permissions, final Directive directive, final String uname, final String gname) throws FileNotFoundException {
    addFile( path, source, permissions, directive, uname, gname, -1);
  }

  /**
   * Adds a file entry to the archive with the specified permissions.
   *
   * @param path the destination path for the installed file.
   * @param source the local file to be included in the package.
   * @param permissions the permissions flags.
   * @param directive directive indicating special handling for this file.
   * @param uname user owner for the given file
   * @param gname group owner for the given file
   * @param dirmode permission flags for parent directories, use -1 to leave as default.
   * @throws java.io.FileNotFoundException file wasn't found
   */
  public synchronized void addFile( final String path, final File source, final int permissions, final Directive directive, final String uname, final String gname, final int dirmode) throws FileNotFoundException {
    addFile( path, source, permissions, directive, uname, gname, dirmode, true);
  }

  /**
   * Adds a file entry to the archive with the specified permissions.
   *
   * @param path the destination path for the installed file.
   * @param source the local file to be included in the package.
   * @param permissions the permissions flags, use -1 to leave as default.
   * @param directive directive indicating special handling for this file, use null to ignore.
   * @param uname user owner for the given file, use null for default user.
   * @param gname group owner for the given file, use null for default group.
   * @param dirmode permission flags for parent directories, use -1 to leave as default.
   * @param addParents whether to create parent directories for the file, defaults to true for other methods.
   * @throws java.io.FileNotFoundException file wasn't found
   */
  public synchronized void addFile( final String path, final File source, final int permissions, final Directive directive, final String uname, final String gname, final int dirmode, final boolean addParents) throws FileNotFoundException {
    if ( files.contains( path)) return;

    if ( addParents) addParents( new File( path), dirmode, uname, gname);
    files.add( path);
    logger.log( FINE, "Adding file ''{0}''.", path);
    CpioHeader header;
    if ( directive != null && (( directive.flag() & Directive.RPMFILE_GHOST ) == Directive.RPMFILE_GHOST ))
      header = new CpioHeader( path);
    else
      header = new CpioHeader( path, source);
    header.setType( FILE);
    header.setInode( inode++);
    if ( null == uname) {
      header.setUname(DEFAULT_USERNAME);
    } else if (0 == uname.length()) {
      header.setUname(DEFAULT_USERNAME);
    } else {
      header.setUname(uname);
    }
    if ( null == gname) {
      header.setGname(DEFAULT_GROUP);
    } else if (0 == gname.length()) {
      header.setGname(DEFAULT_GROUP);
    } else {
      header.setGname(gname);
    }
    if ( -1 == permissions) {
      header.setPermissions(DEFAULT_FILE_PERMISSION);
    } else {
      header.setPermissions( permissions);
    }
    headers.add( header);
    sources.put( header, source);
   
    if ( directive != null) header.setFlags( directive.flag());
  }

  /**
   * Adds a URL entry to the archive with the specified permissions.
   *
   * @param path the destination path for the installed file.
   * @param source the URL with the data to be added
   * @param permissions the permissions flags.
   * @param directive directive indicating special handling for this file.
   * @param uname user owner for the given file
   * @param gname group owner for the given file
   * @param dirmode permission flags for parent directories, use -1 to leave as default.
   * @throws java.io.FileNotFoundException file wasn't found
   */
  public synchronized void addURL( final String path, final URL source, final int permissions, final Directive directive, final String uname, final String gname, final int dirmode) throws FileNotFoundException {
    if ( files.contains( path)) return;

    addParents( new File( path), dirmode, uname, gname);
    files.add( path);
    logger.log( FINE, "Adding file ''{0}''.", path);
    CpioHeader header = new CpioHeader( path, source);
    header.setType( FILE);
    header.setInode( inode++);
    if (uname != null) header.setUname(uname);
    if (gname != null) header.setGname(gname);
    if ( permissions != -1) header.setPermissions( permissions);
    headers.add( header);
    sources.put( header, source);
   
    if ( directive != null) header.setFlags( directive.flag());
  }

  /**
   * Adds entries for parent directories of this file, so that they may be cleaned up when
   * removing the package.
   * @param file the file to add parent directories of
   * @param permissions the permissions flags
   * @param uname user owner for the given file
   * @param gname group owner for the given file
   */
  protected synchronized void addParents( final File file, final int permissions, final String uname, final String gname ) {
    final ArrayList< String> parents = new ArrayList< String>();
    listParents( parents, file);
    for ( String parent : parents) addDirectory( parent, permissions, null, uname, gname);
  }

  /**
   * Add additional directory that is assumed to already exist on system where the RPM will be installed
   * (e.g. /etc) and should not have an entry in the RPM.
   *
   * The directory will be added to all instance of Contents created after this method is called.
   *
   * @param directory the directory to add
   */
  public static synchronized void addBuiltinDirectory( final String directory) {
    BUILTIN.add(directory);
  }
 
  /**
   * Add additional directory that is assumed to already exist on system where the RPM will be installed
   * (e.g. /etc) and should not have an entry in the RPM.
   *
   * The builtin will only be added to this instance of Contents.
   *
   * @param directory the directory to add
   */
  public synchronized void addLocalBuiltinDirectory( final String directory) {
    builtins.add(directory);
  }

  /**
   * Retrieve the size of this archive in number of files. This count includes both directory entries and
   * soft links.
   * @return the number of files in this archive
   */
  public int size() { return headers.size(); }

  /**
   * Retrieve the archive headers. The returned {@link Iterable} will iterate in the correct order for
   * the final archive.
   * @return the headers
   */
  public Iterable< CpioHeader> headers() { return headers; }

  /**
   * Retrieves the content for this archive entry, which may be a {@link File} if the entry is a regular file or
   * a {@link CharSequence} containing the name of the target path if the entry is a link. This is the value to
   * be written to the archive as the body of the entry.
   * @param header the header to get the content from
   * @return the content
   */
  public Object getSource( CpioHeader header) { return sources.get( header); }

  /**
   * Accumulated size of all files included in the archive.
   * @return the size of all files included in the archive
   */
  public int getTotalSize() {
    int total = 0;
    try {
      for ( Object object : sources.values()) {
        if ( object instanceof File) total += (( File) object).length();
        else if ( object instanceof URL) total += (( URL) object).openConnection().getContentLength();
      }
    } catch ( IOException e) {
      throw new RuntimeException( e);
    }
    return total;
  }

  /**
   * Gets the dirnames headers values.
   * @return the dirnames headers values
   */
  public String[] getDirNames() {
    final Set< String> set = new LinkedHashSet< String>();
    for ( CpioHeader header : headers) {
      String path = new File( header.getName()).getParent();
      if ( path == null) continue;

      String parent = normalizePath( path);
      if ( !parent.endsWith( "/")) parent += "/";
      set.add( parent);
    }
    return set.toArray( new String[ set.size()]);
  }

  /**
   * Gets the dirindexes headers values.
   * @return the dirindexes
   */
  // TODO: Fix this (as part of general refactoring) to be much better.
  public int[] getDirIndexes() {
    final List< String> dirs = asList( getDirNames());
    int[] array = new int[ headers.size()];
    int x = 0;
    for ( CpioHeader header : headers) {
      String path = new File( header.getName()).getParent();
      if ( path == null) continue;

      String parent = normalizePath( path);
      if ( !parent.endsWith( "/")) parent += "/";
      array[ x++] = dirs.indexOf( parent);
    }
    return array;
  }

  /**
   * Gets the basenames header values.
   * @return the basename header values
   */
  public String[] getBaseNames() {
    String[] array = new String[ headers.size()];
    int x = 0;
    for ( CpioHeader header : headers) array[ x++] = normalizePath( new File( header.getName()).getName());
    return array;
  }

  /**
   * Gets the sizes header values.
   * @return the sizes header values
   */
  public int[] getSizes() {
    int[] array = new int[ headers.size()];
    int x = 0;
    try {
      for ( CpioHeader header : headers) {
        Object object = sources.get( header);
        if ( object instanceof File) array[ x] = ( int) (( File) object).length();
        else if ( object instanceof URL) array[ x] = (( URL) object).openConnection().getContentLength();
        else if ( header.getType() == DIR) array[ x] = 4096;
        ++x;
      }
    } catch ( IOException e) {
      throw new RuntimeException( e);
    }
    return array;
  }

  /**
   * Gets the modes header values.
   * @return the modes header values
   */
  public short[] getModes() {
    short[] array = new short[ headers.size()];
    int x = 0;
    for ( CpioHeader header : headers) array[ x++] = ( short) header.getMode();
    return array;
  }

  /**
   * Gets the rdevs header values.
   * @return the rdevs header values
   */
  public short[] getRdevs() {
    short[] array = new short[ headers.size()];
    int x = 0;
    for ( CpioHeader header : headers) array[ x++] = ( short) (( header.getRdevMajor() << 8) + header.getRdevMinor());
    return array;
  }

  /**
   * Gets the mtimes header values.
   * @return the mtimes header values
   */
  public int[] getMtimes() {
    int[] array = new int[ headers.size()];
    int x = 0;
    for ( CpioHeader header : headers) {
      array[ x++] = header.getMtime();
    }
    return array;
  }

  /**
   * Caclulates an MD5 hash for each file in the archive.
   * @return the MD5 hashes
   * @throws NoSuchAlgorithmException if the algorithm isn't supported
   * @throws IOException there was an IO error
   */
  public String[] getMD5s() throws NoSuchAlgorithmException, IOException {
    /**
     * This could be more efficiently handled during the output phase using a filtering channel,
     * but would require placeholder values in the archive and some state. This is left for a
     * later refactoring.
     */
    final ByteBuffer buffer = ByteBuffer.allocate( 4096);
    String[] array = new String[ headers.size()];
    int x = 0;
    for ( CpioHeader header : headers) {
      Object object = sources.get( header);
      String value = "";
      if ( object instanceof File) {
        final ReadableChannelWrapper input = new ReadableChannelWrapper( new FileInputStream(( File) object).getChannel());
        final Key< byte[]> key = input.start( "MD5");
        while ( input.read( buffer) != -1) buffer.rewind();
        value = Util.hex(input.finish(key));
        input.close();
      } else if ( object instanceof URL) {
        final ReadableChannelWrapper input = new ReadableChannelWrapper( Channels.newChannel((( URL) object).openConnection().getInputStream()));
        final Key< byte[]> key = input.start( "MD5");
        while ( input.read( buffer) != -1) buffer.rewind();
        value = Util.hex(input.finish(key));
        input.close();
      }
      array[ x++] = value;
    }
    return array;
  }

  /**
   * Gets the linktos header values.
   * @return the linktos header values
   */
  public String[] getLinkTos() {
    String[] array = new String[ headers.size()];
    int x = 0;
    for ( CpioHeader header : headers) {
      Object object = sources.get( header);
      String value = "";
      if ( object instanceof String) value = String.valueOf( object);
      array[ x++] = value;
    }
    return array;
  }

  /**
   * Gets the flags header values.
   * @return the flags header values
   */
  public int[] getFlags() {
    int[] array = new int[ headers.size()];
    int x = 0;
    for ( CpioHeader header : headers) array[ x++] = header.getFlags();
    return array;
  }

  /**
   * Gets the users header values.
   * @return the users header values
   */
  public String[] getUsers() {
    String[] array = new String[ headers.size()];
    int x = 0;
    for (CpioHeader header : headers) {
      array[ x++] = header.getUname() == null ? "root" : header.getUname();
    }
    return array;
  }

  /**
   * Gets the groups header values.
   * @return the groups header values
   */
  public String[] getGroups() {
    String[] array = new String[ headers.size()];
    int x = 0;
    for (CpioHeader header : headers) {
      array[ x++] = header.getGname() == null ? "root" : header.getGname();
    }
    return array;
  }

  /**
   * Gets the colors header values.
   * @return the colors header values
   */
  public int[] getColors() {
    return new int[ headers.size()];
  }

  /**
   * Gets the verifyflags header values.
   * @return the verifyflags header values
   */
  public int[] getVerifyFlags() {
    int[] array = new int[ headers.size()];
    Arrays.fill( array, -1);
    return array;
  }

  /**
   * Gets the classes header values.
   * @return the classes header values
   */
  public int[] getClasses() {
    int[] array = new int[ headers.size()];
    Arrays.fill( array, 1);
    return array;
  }

  /**
   * Gets the devices header values.
   * @return the devices header values
   */
  public int[] getDevices() {
    int[] array = new int[ headers.size()];
    int x = 0;
    for ( CpioHeader header : headers) array[ x++] = ( header.getDevMajor() << 8) + header.getDevMinor();
    return array;
  }

  /**
   * Gets the inodes header values.
   * @return the iNodes header values
   */
  public int[] getInodes() {
    int[] array = new int[ headers.size()];
    int x = 0;
    for ( CpioHeader header : headers) array[ x++] = header.getInode();
    return array;
  }

  /**
   * Gets the langs header values.
   * @return the langs header values
   */
  public String[] getLangs() {
    String[] array = new String[ headers.size()];
    Arrays.fill( array, "");
    return array;
  }

  /**
   * Gets the dependsx header values.
   * @return the dependsx header values
   */
  public int[] getDependsX() {
    return new int[ headers.size()];
  }

  /**
   * Gets the dependsn header values.
   * @return the dependsn header values
   */
  public int[] getDependsN() {
    return new int[ headers.size()];
  }

  /**
   * Gets the contexts header values.
   * @return the contexts header values
   */
  public String[] getContexts() {
    String[] array = new String[ headers.size()];
    Arrays.fill( array, "<<none>>");
    return array;
  }

  /**
   * Generates a list of parent paths given a starting path.
   * @param parents the list to add the parents to
   * @param file the file to search for parents of
   */
  protected void listParents( final List< String> parents, final File file) {
    final File parent = file.getParentFile();
    if ( parent == null) return;
   
    final String path = normalizePath( parent.getPath());
    if ( builtins.contains( path)) return;

    parents.add( path);
    listParents( parents, parent);
  }

  /**
   * Comparator that orders files in the CPIO archive by their file name
   * as present in th header.
   */
  private static class HeaderComparator implements Comparator< CpioHeader> {
    public int compare( final CpioHeader one, final CpioHeader two) {
      return one.getName().compareTo( two.getName());
    }
    public boolean equals( final CpioHeader one, final CpioHeader two) {
      return one.getName().equals( two.getName());
    }
  }
}
TOP

Related Classes of org.redline_rpm.payload.Contents$HeaderComparator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.