Example #1
0
public class FileDescLoader {
  private static final Logger log = LoggerFactory.getLogger(FileDescLoader.class);
  private static final ExecutorService exec = Executors.newCachedThreadPool();

  public static void load(Collection<FileDesc> files, Path root, int blocSize, Pattern pattern)
      throws IOException {
    root = root.toAbsolutePath().normalize();
    Visitor visitor = new Visitor(root, blocSize, pattern);
    Files.walkFileTree(root, visitor);
    for (Future<FileDesc> future : visitor.futures()) {
      try {
        files.add(future.get());
      } catch (Exception e) {
        log.error("", e);
      }
    }
  }

  /** @return matching bloc B bloc -> A bloc */
  public static Map<Integer, Integer> diff(FileDesc a, FileDesc b) {
    Map<Integer, List<IndexedHash>> blocA = new HashMap<Integer, List<IndexedHash>>();
    int i = 0;
    for (Bloc bloc : a.blocs) {
      List<IndexedHash> l = blocA.get(bloc.roll);
      if (l == null) {
        l = new ArrayList<IndexedHash>();
        blocA.put(bloc.roll, l);
      }
      l.add(new IndexedHash(i++, bloc.hash));
    }

    Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    loop:
    for (i = 0; i < b.blocs.length; i++) {
      Bloc blocB = b.blocs[i];
      List<IndexedHash> list = blocA.get(blocB.roll);
      if (list != null) {
        for (IndexedHash bloc : list) {
          if (blocB.hash.equals(bloc.h)) {
            map.put(i, bloc.i);
            continue loop;
          }
        }
      }
    }
    return map;
  }

  public static FileDesc loadFile(Path root, Path file, int blocSize)
      throws NoSuchAlgorithmException, FileNotFoundException, IOException {
    MessageDigest md = MessageDigest.getInstance("SHA-512");
    MessageDigest fileMd = MessageDigest.getInstance("SHA-512");

    FileDesc desc = new FileDesc(file.toString(), null, null);
    List<Bloc> list = new ArrayList<Bloc>();
    try (FileInputStream fis = new FileInputStream(root.resolve(file).toString())) {
      byte[] buf = new byte[blocSize];
      byte[] h;
      int s;
      while ((s = fis.read(buf)) != -1) {
        int c;
        while (s < buf.length && (c = fis.read()) != -1) buf[s++] = (byte) c;
        fileMd.update(buf, 0, s);

        // padding
        byte p = 0;
        while (s < buf.length) buf[s++] = ++p;
        h = md.digest(buf);
        Bloc bloc = new Bloc(RollingChecksum.compute(buf), new Hash(h));
        list.add(bloc);
      }
      h = fileMd.digest();
      desc.fileHash = new Hash(h);
      desc.blocs = list.toArray(new Bloc[0]);
    }
    return desc;
  }

  private static class Visitor implements FileVisitor<Path> {
    private Path root;
    private int blocSize;
    private Matcher m;
    private List<Future<FileDesc>> futures = new ArrayList<>();

    public Visitor(Path root, int blocFile, Pattern pattern) {
      this.root = root;
      this.blocSize = blocFile;
      this.m = pattern == null ? null : pattern.matcher("");
    }

    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
        throws IOException {
      if (m != null) m.reset(root.relativize(dir).toString());
      return m == null || m.matches() || m.hitEnd()
          ? FileVisitResult.CONTINUE
          : FileVisitResult.SKIP_SUBTREE;
    }

    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
      if (attrs.isRegularFile()) {
        if (m != null) m.reset(root.relativize(file).toString());
        if (m == null || m.matches())
          futures.add(exec.submit(new FileLoader(root, file, blocSize)));
      }
      return FileVisitResult.CONTINUE;
    }

    public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
      return FileVisitResult.CONTINUE;
    }

    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
      return FileVisitResult.CONTINUE;
    }

    public List<Future<FileDesc>> futures() {
      return futures;
    }
  }

  private static class FileLoader implements Callable<FileDesc> {
    private Path file;
    private int blocSize;
    private Path root;

    public FileLoader(Path root, Path file, int blocSize) throws IOException {
      this.root = root;
      this.file = file;
      this.blocSize = blocSize;
    }

    public FileDesc call() throws Exception {
      return loadFile(root, root.relativize(file), blocSize);
    }
  }

  public static class IndexedHash {
    public int i;
    public Hash h;

    public IndexedHash(int i, Hash h) {
      this.i = i;
      this.h = h;
    }
  }
}
/**
 * A SecureRandom implementation that is able to override the standard JVM provided implementation,
 * and which simply serves random numbers by reading /dev/urandom. That is, it delegates to the
 * kernel on UNIX systems and is unusable on other platforms. Attempts to manually set the seed are
 * ignored. There is no difference between seed bytes and non-seed bytes, they are all from the same
 * source.
 */
public class LinuxSecureRandom extends SecureRandomSpi {
  private static final FileInputStream urandom;

  private static class LinuxSecureRandomProvider extends Provider {
    public LinuxSecureRandomProvider() {
      super(
          "LinuxSecureRandom",
          1.0,
          "A Linux specific random number provider that uses /dev/urandom");
      put("SecureRandom.LinuxSecureRandom", LinuxSecureRandom.class.getName());
    }
  }

  private static final Logger log = LoggerFactory.getLogger(LinuxSecureRandom.class);

  static {
    try {
      File file = new File("/dev/urandom");
      // This stream is deliberately leaked.
      urandom = new FileInputStream(file);
      if (urandom.read() == -1) throw new RuntimeException("/dev/urandom not readable?");
      // Now override the default SecureRandom implementation with this one.
      int position = Security.insertProviderAt(new LinuxSecureRandomProvider(), 1);

      if (position != -1) log.info("Secure randomness will be read from {} only.", file);
      else log.info("Randomness is already secure.");
    } catch (FileNotFoundException e) {
      // Should never happen.
      log.error("/dev/urandom does not appear to exist or is not openable");
      throw new RuntimeException(e);
    } catch (IOException e) {
      log.error("/dev/urandom does not appear to be readable");
      throw new RuntimeException(e);
    }
  }

  private final DataInputStream dis;

  public LinuxSecureRandom() {
    // DataInputStream is not thread safe, so each random object has its own.
    dis = new DataInputStream(urandom);
  }

  @Override
  protected void engineSetSeed(byte[] bytes) {
    // Ignore.
  }

  @Override
  protected void engineNextBytes(byte[] bytes) {
    try {
      dis.readFully(bytes); // This will block until all the bytes can be read.
    } catch (IOException e) {
      throw new RuntimeException(e); // Fatal error. Do not attempt to recover from this.
    }
  }

  @Override
  protected byte[] engineGenerateSeed(int i) {
    byte[] bits = new byte[i];
    engineNextBytes(bits);
    return bits;
  }
}