    public void process(InputStream in, ZipEntry zipEntry) throws IOException {
      String root = getRootName(zipEntry.getName());
      if (rootDir == null) {
        rootDir = root;
      }
      else if (!rootDir.equals(root)) {
        throw new ZipException("Unwrapping with multiple roots is not supported, roots: " + rootDir + ", " + root);
      }

      String name = mapper.map(getUnrootedName(root, zipEntry.getName()));
      if (name != null) {
        File file = new File(outputDir, name);

        /* If we see the relative traversal string of ".." we need to make sure
         * that the outputdir + name doesn't leave the outputdir. See
         * DirectoryTraversalMaliciousTest for details.
         */
        if (name.indexOf("..") != -1 && !file.getCanonicalPath().startsWith(outputDir.getCanonicalPath())) {
          throw new ZipException("The file "+name+" is trying to leave the target output directory of "+outputDir+". Ignoring this file.");
        }

        if (zipEntry.isDirectory()) {
          FileUtils.forceMkdir(file);
        }
        else {
          FileUtils.forceMkdir(file.getParentFile());

          if (log.isDebugEnabled() && file.exists()) {
            log.debug("Overwriting file '{}'.", zipEntry.getName());
          }

          FileUtils.copy(in, file);
        }
      }
    }
The software uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the software does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.