<|start_filename|>src/test/java/org/codehaus/plexus/archiver/tar/TarFileTest.java<|end_filename|>
package org.codehaus.plexus.archiver.tar;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Enumeration;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.codehaus.plexus.PlexusTestCase;
import org.codehaus.plexus.archiver.Archiver;
import org.codehaus.plexus.archiver.bzip2.BZip2Compressor;
import org.codehaus.plexus.archiver.gzip.GZipCompressor;
import org.codehaus.plexus.archiver.util.Compressor;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.IOUtil;
import static org.codehaus.plexus.components.io.resources.ResourceFactory.createResource;

/**
 * Test case for {@link TarFile}.
 */
public class TarFileTest
    extends PlexusTestCase
{

    private interface TarFileCreator
    {

        TarFile newTarFile( File file ) throws IOException;

    }

    /**
     * Test for the uncompressed tar file, {@link TarFile}.
     */
    public void testTarFile() throws Exception
    {
        testTarFile( null, null, new TarFileCreator()
        {

            @Override
            public TarFile newTarFile( File file )
                throws IOException
            {
                return new TarFile( file );
            }

        } );
    }

    /**
     * Test for the gzip compressed tar file, {@link GZipTarFile}.
     */
    public void testGZipTarFile() throws Exception
    {
        final GZipCompressor compressor = new GZipCompressor();
        testTarFile( compressor, ".gz", new TarFileCreator()
        {

            @Override
            public TarFile newTarFile( File file )
                throws IOException
            {
                return new GZipTarFile( file );
            }

        } );
    }

    /**
     * Test for the bzip2 compressed tar file, {@link BZip2TarFile}.
     */
    public void testBZip2TarFile() throws Exception
    {
        final BZip2Compressor compressor = new BZip2Compressor();
        testTarFile( compressor, ".bz2", new TarFileCreator()
        {

            @Override
            public TarFile newTarFile( File file )
                throws IOException
            {
                return new BZip2TarFile( file );
            }

        } );
    }

    private void testTarFile( Compressor compressor, String extension,
                              TarFileCreator tarFileCreator )
        throws Exception
    {
        File file = new File( "target/output/TarFileTest.tar" );
        final TarArchiver archiver = (TarArchiver) lookup( Archiver.ROLE, "tar" );
        archiver.setLongfile( TarLongFileMode.posix );
        archiver.setDestFile( file );
        archiver.addDirectory( new File( "src" ) );
        FileUtils.removePath( file.getPath() );
        archiver.createArchive();
        if ( compressor != null )
        {
            final File compressedFile = new File( file.getPath() + extension );
            compressor.setSource( createResource( file, file.getName() ) );
            compressor.setDestFile( compressedFile );
            compressor.compress();
            compressor.close();
            file = compressedFile;
        }
        final TarFile tarFile = tarFileCreator.newTarFile( file );

        for ( Enumeration en = tarFile.getEntries(); en.hasMoreElements(); )
        {
            final TarArchiveEntry te = (TarArchiveEntry) en.nextElement();
            if ( te.isDirectory() || te.isSymbolicLink() )
            {
                continue;
            }
            final File teFile = new File( "src", te.getName() );
            final InputStream teStream = tarFile.getInputStream( te );
            final InputStream fileStream = Files.newInputStream( teFile.toPath() );
            assertTrue( Arrays.equals( IOUtil.toByteArray( teStream ), IOUtil.toByteArray( fileStream ) ) );
            teStream.close();
            fileStream.close();
        }

        tarFile.close();
    }

}


<|start_filename|>src/test/java/org/codehaus/plexus/archiver/jar/BaseJarArchiverTest.java<|end_filename|>
/**
 *
 * Copyright 2018 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.codehaus.plexus.archiver.jar;

import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.util.IOUtil;
import org.junit.Test;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

public abstract class BaseJarArchiverTest
{

    /*
     * Verify that the JarArchiver implementation
     * could create basic JAR file
     */
    @Test
    public void testCreateJar()
        throws IOException, ArchiverException
    {
        File jarFile = new File( "target/output/testJar.jar" );
        jarFile.delete();

        JarArchiver archiver = getJarArchiver();
        archiver.setDestFile( jarFile );
        archiver.addDirectory( new File( "src/test/resources/java-classes" ) );

        archiver.createArchive();

        // verify that the JAR file is created and contains the expected files
        try ( ZipFile resultingArchive = new ZipFile( jarFile ) )
        {
            // verify that the JAR file contains manifest file
            assertNotNull( resultingArchive.getEntry( "META-INF/MANIFEST.MF" )  );

            // verify the JAR contains the class and it is not corrupted
            ZipEntry classFileEntry = resultingArchive.getEntry( "com/example/app/Main.class" );
            InputStream resultingClassFile = resultingArchive.getInputStream( classFileEntry );
            InputStream originalClassFile =
                Files.newInputStream( Paths.get( "src/test/resources/java-classes/com/example/app/Main.class" ) );

            assertTrue( IOUtil.contentEquals( originalClassFile, resultingClassFile ) );
        }
    }

    protected abstract JarArchiver getJarArchiver();

}


<|start_filename|>src/main/java/org/codehaus/plexus/archiver/AbstractArchiver.java<|end_filename|>
/**
 *
 * Copyright 2004 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.codehaus.plexus.archiver;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.UndeclaredThrowableException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import javax.annotation.Nonnull;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.archiver.manager.ArchiverManager;
import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.components.io.attributes.PlexusIoResourceAttributes;
import org.codehaus.plexus.components.io.attributes.SimpleResourceAttributes;
import org.codehaus.plexus.components.io.functions.ResourceAttributeSupplier;
import org.codehaus.plexus.components.io.resources.AbstractPlexusIoResourceCollection;
import org.codehaus.plexus.components.io.resources.EncodingSupported;
import org.codehaus.plexus.components.io.resources.PlexusIoArchivedResourceCollection;
import org.codehaus.plexus.components.io.resources.PlexusIoFileResourceCollection;
import org.codehaus.plexus.components.io.resources.PlexusIoResource;
import org.codehaus.plexus.components.io.resources.PlexusIoResourceCollection;
import org.codehaus.plexus.components.io.resources.proxy.PlexusIoProxyResourceCollection;
import org.codehaus.plexus.context.Context;
import org.codehaus.plexus.context.ContextException;
import org.codehaus.plexus.logging.AbstractLogEnabled;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.logging.console.ConsoleLogger;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
import static org.codehaus.plexus.archiver.util.DefaultArchivedFileSet.archivedFileSet;
import static org.codehaus.plexus.archiver.util.DefaultFileSet.fileSet;

public abstract class AbstractArchiver
    extends AbstractLogEnabled
    implements Archiver, Contextualizable, FinalizerEnabled
{

    private Logger logger;

    private File destFile;

    /**
     * A list of the following objects:
     * <ul>
     * <li>Instances of {@link ArchiveEntry}, which are passed back by {@link #getResources()} without modifications
     * .</li>
     * <li>Instances of {@link PlexusIoResourceCollection}, which are converted into an {@link Iterator} over instances
     * of {@link ArchiveEntry} by {@link #getResources()}.
     * </ul>
     */
    private final List<Object> resources = new ArrayList<>();

    private boolean includeEmptyDirs = true;

    private int forcedFileMode = -1; // Will always be used

    private int forcedDirectoryMode = -1; // Will always be used

    private int defaultFileMode = -1; // Optionally used if a value is needed

    private int defaultDirectoryMode = -1; // Optionally used if a value is needed

    private boolean forced = true;

    private List<ArchiveFinalizer> finalizers;

    private File dotFileDirectory;

    private String duplicateBehavior = Archiver.DUPLICATES_SKIP;

    // On lunix-like systems, we replace windows backslashes with forward slashes
    private final boolean replacePathSlashesToJavaPaths = File.separatorChar == '/';

    private final List<Closeable> closeables = new ArrayList<>();

    /**
     * since 2.2 is on by default
     *
     * @since 1.1
     */
    private boolean useJvmChmod = true;

    /**
     * @since 4.2.0
     */
    private Date lastModifiedDate;

    /**
     * @since 4.2.0
     */
    private Comparator<String> filenameComparator;

    /**
     * @since 4.2.0
     */
    private int overrideUid = -1;

    /**
     * @since 4.2.0
     */
    private String overrideUserName;

    /**
     * @since 4.2.0
     */
    private int overrideGid = -1;

    /**
     * @since 4.2.0
     */
    private String overrideGroupName;

    // contextualized.
    private ArchiverManager archiverManager;

    private static class AddedResourceCollection
    {

        private final PlexusIoResourceCollection resources;

        private final int forcedFileMode;

        private final int forcedDirectoryMode;

        public AddedResourceCollection( PlexusIoResourceCollection resources, int forcedFileMode, int forcedDirMode )
        {
            this.resources = resources;
            this.forcedFileMode = forcedFileMode;
            this.forcedDirectoryMode = forcedDirMode;
        }

        private int maybeOverridden( int suggestedMode, boolean isDir )
        {
            if ( isDir )
            {
                return forcedDirectoryMode >= 0 ? forcedDirectoryMode : suggestedMode;
            }
            else
            {
                return forcedFileMode >= 0 ? forcedFileMode : suggestedMode;

            }
        }

    }

    /**
     * @since 1.1
     */
    private boolean ignorePermissions = false;

    @Override
    public String getDuplicateBehavior()
    {
        return duplicateBehavior;
    }

    @Override
    public void setDuplicateBehavior( final String duplicate )
    {
        if ( !Archiver.DUPLICATES_VALID_BEHAVIORS.contains( duplicate ) )
        {
            throw new IllegalArgumentException(
                "Invalid duplicate-file behavior: \'" + duplicate + "\'. Please specify one of: "
                    + Archiver.DUPLICATES_VALID_BEHAVIORS );
        }

        duplicateBehavior = duplicate;
    }

    @Override
    public final void setFileMode( final int mode )
    {
        if ( mode >= 0 )
        {
            forcedFileMode = ( mode & UnixStat.PERM_MASK ) | UnixStat.FILE_FLAG;
        }
        else
        {
            forcedFileMode = -1;
        }
    }

    @Override
    public final void setDefaultFileMode( final int mode )
    {
        defaultFileMode = ( mode & UnixStat.PERM_MASK ) | UnixStat.FILE_FLAG;
    }

    @Override
    public final int getOverrideFileMode()
    {
        return forcedFileMode;
    }

    @Override
    public final int getFileMode()
    {
        if ( forcedFileMode < 0 )
        {
            if ( defaultFileMode < 0 )
            {
                return DEFAULT_FILE_MODE;
            }

            return defaultFileMode;
        }

        return forcedFileMode;
    }

    @Override
    public final int getDefaultFileMode()
    {
        return defaultFileMode;
    }

    /**
     * @deprecated Use {@link Archiver#getDefaultFileMode()}.
     */
    @Deprecated
    public final int getRawDefaultFileMode()
    {
        return getDefaultFileMode();
    }

    @Override
    public final void setDirectoryMode( final int mode )
    {
        if ( mode >= 0 )
        {
            forcedDirectoryMode = ( mode & UnixStat.PERM_MASK ) | UnixStat.DIR_FLAG;
        }
        else
        {
            forcedDirectoryMode = -1;
        }
    }

    @Override
    public final void setDefaultDirectoryMode( final int mode )
    {
        defaultDirectoryMode = ( mode & UnixStat.PERM_MASK ) | UnixStat.DIR_FLAG;
    }

    @Override
    public final int getOverrideDirectoryMode()
    {
        return forcedDirectoryMode;
    }

    @Override
    public final int getDirectoryMode()
    {
        if ( forcedDirectoryMode < 0 )
        {
            if ( defaultDirectoryMode < 0 )
            {
                return DEFAULT_DIR_MODE;
            }

            return defaultDirectoryMode;
        }

        return forcedDirectoryMode;
    }

    @Override
    public final int getDefaultDirectoryMode()
    {
        if ( defaultDirectoryMode < 0 )
        {
            return DEFAULT_DIR_MODE;
        }
        else
        {
            return defaultDirectoryMode;
        }
    }

    @Override
    public boolean getIncludeEmptyDirs()
    {
        return includeEmptyDirs;
    }

    @Override
    public void setIncludeEmptyDirs( final boolean includeEmptyDirs )
    {
        this.includeEmptyDirs = includeEmptyDirs;
    }

    @Override
    public void addDirectory( @Nonnull final File directory )
        throws ArchiverException
    {
        addFileSet(
            fileSet( directory ).prefixed( "" ).includeExclude( null, null ).includeEmptyDirs( includeEmptyDirs ) );
    }

    @Override
    public void addDirectory( @Nonnull final File directory, final String prefix )
        throws ArchiverException
    {
        addFileSet(
            fileSet( directory ).prefixed( prefix ).includeExclude( null, null ).includeEmptyDirs( includeEmptyDirs ) );
    }

    @Override
    public void addDirectory( @Nonnull final File directory, final String[] includes, final String[] excludes )
        throws ArchiverException
    {
        addFileSet( fileSet( directory ).prefixed( "" ).includeExclude( includes, excludes ).includeEmptyDirs(
            includeEmptyDirs ) );
    }

    @Override
    public void addDirectory( @Nonnull final File directory, final String prefix, final String[] includes,
                              final String[] excludes )
        throws ArchiverException
    {
        addFileSet( fileSet( directory ).prefixed( prefix ).includeExclude( includes, excludes ).includeEmptyDirs(
            includeEmptyDirs ) );
    }

    @Override
    public void addFileSet( @Nonnull final FileSet fileSet )
        throws ArchiverException
    {
        final File directory = fileSet.getDirectory();
        if ( directory == null )
        {
            throw new ArchiverException( "The file sets base directory is null." );
        }

        if ( !directory.isDirectory() )
        {
            throw new ArchiverException( directory.getAbsolutePath() + " isn't a directory." );
        }

        // The PlexusIoFileResourceCollection contains platform-specific File.separatorChar which
        // is an interesting cause of grief, see PLXCOMP-192
        final PlexusIoFileResourceCollection collection = new PlexusIoFileResourceCollection();
        collection.setFollowingSymLinks( false );

        collection.setIncludes( fileSet.getIncludes() );
        collection.setExcludes( fileSet.getExcludes() );
        collection.setBaseDir( directory );
        collection.setFileSelectors( fileSet.getFileSelectors() );
        collection.setIncludingEmptyDirectories( fileSet.isIncludingEmptyDirectories() );
        collection.setPrefix( fileSet.getPrefix() );
        collection.setCaseSensitive( fileSet.isCaseSensitive() );
        collection.setUsingDefaultExcludes( fileSet.isUsingDefaultExcludes() );
        collection.setStreamTransformer( fileSet.getStreamTransformer() );
        collection.setFileMappers( fileSet.getFileMappers() );
        collection.setFilenameComparator( getFilenameComparator() );

        if ( getOverrideDirectoryMode() > -1 || getOverrideFileMode() > -1 || getOverrideUid() > -1
            || getOverrideGid() > -1 || getOverrideUserName() != null || getOverrideGroupName() != null )
        {
            collection.setOverrideAttributes( getOverrideUid(), getOverrideUserName(), getOverrideGid(),
                                              getOverrideGroupName(), getOverrideFileMode(),
                                              getOverrideDirectoryMode() );
        }

        if ( getDefaultDirectoryMode() > -1 || getDefaultFileMode() > -1 )
        {
            collection.setDefaultAttributes( -1, null, -1, null, getDefaultFileMode(), getDefaultDirectoryMode() );
        }

        addResources( collection );
    }

    @Override
    public void addFile( @Nonnull final File inputFile, @Nonnull final String destFileName )
        throws ArchiverException
    {
        final int fileMode = getOverrideFileMode();

        addFile( inputFile, destFileName, fileMode );
    }

    @Override
    public void addSymlink( String symlinkName, String symlinkDestination )
        throws ArchiverException
    {
        final int fileMode = getOverrideFileMode();

        addSymlink( symlinkName, fileMode, symlinkDestination );
    }

    @Override
    public void addSymlink( String symlinkName, int permissions, String symlinkDestination )
        throws ArchiverException
    {
        doAddResource(
            ArchiveEntry.createSymlinkEntry( symlinkName, permissions, symlinkDestination, getDirectoryMode() ) );
    }

    private ArchiveEntry updateArchiveEntryAttributes( ArchiveEntry entry )
    {
        if ( getOverrideUid() > -1 || getOverrideGid() > -1 || getOverrideUserName() != null
            || getOverrideGroupName() != null )
        {
            entry.setResourceAttributes( new SimpleResourceAttributes( getOverrideUid(), getOverrideUserName(),
                                                                       getOverrideGid(), getOverrideGroupName(),
                                                                       entry.getMode() ) );
        }
        return entry;
    }

    protected ArchiveEntry asArchiveEntry( @Nonnull final PlexusIoResource resource, final String destFileName,
                                           final int permissions, PlexusIoResourceCollection collection )
        throws ArchiverException
    {
        if ( !resource.isExisting() )
        {
            throw new ArchiverException( resource.getName() + " not found." );
        }

        ArchiveEntry entry;
        if ( resource.isFile() )
        {
            entry = ArchiveEntry.createFileEntry( destFileName, resource, permissions, collection, getDirectoryMode() );
        }
        else
        {
            entry = ArchiveEntry.createDirectoryEntry( destFileName, resource, permissions, getDirectoryMode() );
        }

        return updateArchiveEntryAttributes( entry );
    }

    private ArchiveEntry asArchiveEntry( final AddedResourceCollection collection, final PlexusIoResource resource )
        throws ArchiverException
    {
        final String destFileName = collection.resources.getName( resource );

        int fromResource = PlexusIoResourceAttributes.UNKNOWN_OCTAL_MODE;
        if ( resource instanceof ResourceAttributeSupplier )
        {
            final PlexusIoResourceAttributes attrs = ( (ResourceAttributeSupplier) resource ).getAttributes();

            if ( attrs != null )
            {
                fromResource = attrs.getOctalMode();
            }
        }

        return asArchiveEntry( resource, destFileName,
                               collection.maybeOverridden( fromResource, resource.isDirectory() ),
                               collection.resources );
    }

    @Override
    public void addResource( final PlexusIoResource resource, final String destFileName, final int permissions )
        throws ArchiverException
    {
        doAddResource( asArchiveEntry( resource, destFileName, permissions, null ) );
    }

    @Override
    public void addFile( @Nonnull final File inputFile, @Nonnull String destFileName, int permissions )
        throws ArchiverException
    {
        if ( !inputFile.isFile() || !inputFile.exists() )
        {
            throw new ArchiverException( inputFile.getAbsolutePath() + " isn't a file." );
        }

        if ( replacePathSlashesToJavaPaths )
        {
            destFileName = destFileName.replace( '\\', '/' );
        }

        if ( permissions < 0 )
        {
            permissions = getOverrideFileMode();
        }

        try
        {
            // do a null check here, to avoid creating a file stream if there are no filters...
            ArchiveEntry entry =
                ArchiveEntry.createFileEntry( destFileName, inputFile, permissions, getDirectoryMode() );
            doAddResource( updateArchiveEntryAttributes( entry ) );
        }
        catch ( final IOException e )
        {
            throw new ArchiverException( "Failed to determine inclusion status for: " + inputFile, e );
        }
    }

    @Nonnull
    @Override
    public ResourceIterator getResources()
        throws ArchiverException
    {
        return new ResourceIterator()
        {

            private final Iterator addedResourceIter = resources.iterator();

            private AddedResourceCollection currentResourceCollection;

            private Iterator ioResourceIter;

            private ArchiveEntry nextEntry;

            private final Set<String> seenEntries = new HashSet<String>();

            @Override
            public boolean hasNext()
            {
                do
                {
                    if ( nextEntry == null )
                    {
                        if ( ioResourceIter == null )
                        {
                            if ( addedResourceIter.hasNext() )
                            {
                                final Object o = addedResourceIter.next();
                                if ( o instanceof ArchiveEntry )
                                {
                                    nextEntry = (ArchiveEntry) o;
                                }
                                else if ( o instanceof AddedResourceCollection )
                                {
                                    currentResourceCollection = (AddedResourceCollection) o;

                                    try
                                    {
                                        ioResourceIter = currentResourceCollection.resources.getResources();
                                    }
                                    catch ( final IOException e )
                                    {
                                        throw new ArchiverException( e.getMessage(), e );
                                    }
                                }
                                else
                                {
                                    return throwIllegalResourceType( o );
                                }
                            }
                            else
                            {
                                nextEntry = null;
                            }
                        }
                        else
                        {
                            if ( ioResourceIter.hasNext() )
                            {
                                final PlexusIoResource resource = (PlexusIoResource) ioResourceIter.next();
                                nextEntry = asArchiveEntry( currentResourceCollection, resource );
                            }
                            else
                            {
                                // this will leak handles in the IO iterator if the iterator is not fully consumed.
                                // alternately we'd have to make this method return a Closeable iterator back
                                // to the client and ditch the whole issue onto the client.
                                // this does not really make any sense either, might equally well change the
                                // api into something that is not broken by design.
                                addCloseable( ioResourceIter );
                                ioResourceIter = null;
                            }
                        }
                    }

                    if ( nextEntry != null && seenEntries.contains( normalizedForDuplicateCheck( nextEntry ) ) )
                    {
                        final String path = nextEntry.getName();

                        if ( Archiver.DUPLICATES_PRESERVE.equals( duplicateBehavior )
                                 || Archiver.DUPLICATES_SKIP.equals( duplicateBehavior ) )
                        {
                            if ( nextEntry.getType() == ArchiveEntry.FILE )
                            {
                                getLogger().debug( path + " already added, skipping" );
                            }

                            nextEntry = null;
                        }
                        else if ( Archiver.DUPLICATES_FAIL.equals( duplicateBehavior ) )
                        {
                            throw new ArchiverException(
                                "Duplicate file " + path + " was found and the duplicate " + "attribute is 'fail'." );
                        }
                        else
                        {
                            // duplicate equal to add, so we continue
                            getLogger().debug( "duplicate file " + path + " found, adding." );
                        }
                    }
                }
                while ( nextEntry == null && !( ioResourceIter == null && !addedResourceIter.hasNext() ) );

                return nextEntry != null;
            }

            private boolean throwIllegalResourceType( Object o )
            {
                throw new IllegalStateException(
                    "An invalid resource of type: " + o.getClass().getName() + " was added to archiver: "
                        + getClass().getName() );
            }

            @Override
            public ArchiveEntry next()
            {
                if ( !hasNext() )
                {
                    throw new NoSuchElementException();
                }

                final ArchiveEntry next = nextEntry;
                nextEntry = null;

                seenEntries.add( normalizedForDuplicateCheck( next ) );

                return next;
            }

            @Override
            public void remove()
            {
                throw new UnsupportedOperationException( "Does not support iterator" );
            }

            private String normalizedForDuplicateCheck( ArchiveEntry entry )
            {
                return entry.getName().replace( '\\', '/' );
            }

        };

    }

    private static void closeIfCloseable( Object resource )
        throws IOException
    {
        if ( resource == null )
        {
            return;
        }
        if ( resource instanceof Closeable )
        {
            ( (Closeable) resource ).close();
        }

    }

    private static void closeQuietlyIfCloseable( Object resource )
    {
        try
        {
            closeIfCloseable( resource );
        }
        catch ( IOException e )
        {
            throw new RuntimeException( e );
        }
    }

    @Override
    public Map<String, ArchiveEntry> getFiles()
    {
        try
        {
            final Map<String, ArchiveEntry> map = new HashMap<>();
            for ( final ResourceIterator iter = getResources(); iter.hasNext(); )
            {
                final ArchiveEntry entry = iter.next();
                if ( includeEmptyDirs || entry.getType() == ArchiveEntry.FILE )
                {
                    map.put( entry.getName(), entry );
                }
            }
            return map;
        }
        catch ( final ArchiverException e )
        {
            throw new UndeclaredThrowableException( e );
        }
    }

    @Override
    public File getDestFile()
    {
        return destFile;
    }

    @Override
    public void setDestFile( final File destFile )
    {
        this.destFile = destFile;

        if ( destFile != null && destFile.getParentFile() != null )
        {
            destFile.getParentFile().mkdirs();
        }
    }

    @Override
    protected Logger getLogger()
    {
        if ( logger == null )
        {
            if ( super.getLogger() != null )
            {
                logger = super.getLogger();
            }
            else
            {
                logger = new ConsoleLogger( Logger.LEVEL_INFO, "console" );
            }
        }

        return logger;
    }

    protected PlexusIoResourceCollection asResourceCollection( final ArchivedFileSet fileSet, Charset charset )
        throws ArchiverException
    {
        final File archiveFile = fileSet.getArchive();

        final PlexusIoResourceCollection resources;
        try
        {
            resources = archiverManager.getResourceCollection( archiveFile );
        }
        catch ( final NoSuchArchiverException e )
        {
            throw new ArchiverException(
                "Error adding archived file-set. PlexusIoResourceCollection not found for: " + archiveFile, e );
        }

        if ( resources instanceof EncodingSupported )
        {
            ( (EncodingSupported) resources ).setEncoding( charset );
        }

        if ( resources instanceof PlexusIoArchivedResourceCollection )
        {
            ( (PlexusIoArchivedResourceCollection) resources ).setFile( fileSet.getArchive() );
        }
        else
        {
            throw new ArchiverException( "Expected " + PlexusIoArchivedResourceCollection.class.getName() + ", got "
                                             + resources.getClass().getName() );
        }

        if ( resources instanceof AbstractPlexusIoResourceCollection )
        {
            ( (AbstractPlexusIoResourceCollection) resources ).setStreamTransformer( fileSet.getStreamTransformer() );
        }
        final PlexusIoProxyResourceCollection proxy = new PlexusIoProxyResourceCollection( resources );

        proxy.setExcludes( fileSet.getExcludes() );
        proxy.setIncludes( fileSet.getIncludes() );
        proxy.setIncludingEmptyDirectories( fileSet.isIncludingEmptyDirectories() );
        proxy.setCaseSensitive( fileSet.isCaseSensitive() );
        proxy.setPrefix( fileSet.getPrefix() );
        proxy.setUsingDefaultExcludes( fileSet.isUsingDefaultExcludes() );
        proxy.setFileSelectors( fileSet.getFileSelectors() );
        proxy.setStreamTransformer( fileSet.getStreamTransformer() );
        proxy.setFileMappers( fileSet.getFileMappers() );

        if ( getOverrideDirectoryMode() > -1 || getOverrideFileMode() > -1 )
        {
            proxy.setOverrideAttributes( -1, null, -1, null, getOverrideFileMode(), getOverrideDirectoryMode() );
        }

        if ( getDefaultDirectoryMode() > -1 || getDefaultFileMode() > -1 )
        {
            proxy.setDefaultAttributes( -1, null, -1, null, getDefaultFileMode(), getDefaultDirectoryMode() );
        }

        return proxy;
    }

    /**
     * Adds a resource collection to the archive.
     */
    @Override
    public void addResources( final PlexusIoResourceCollection collection )
        throws ArchiverException
    {
        doAddResource( new AddedResourceCollection( collection, forcedFileMode, forcedDirectoryMode ) );
    }

    private void doAddResource( Object item )
    {
        resources.add( item );
    }

    @Override
    public void addArchivedFileSet( final ArchivedFileSet fileSet )
        throws ArchiverException
    {
        final PlexusIoResourceCollection resourceCollection = asResourceCollection( fileSet, null );
        addResources( resourceCollection );
    }

    @Override
    public void addArchivedFileSet( final ArchivedFileSet fileSet, Charset charset )
        throws ArchiverException
    {
        final PlexusIoResourceCollection resourceCollection = asResourceCollection( fileSet, charset );
        addResources( resourceCollection );
    }

    /**
     * @since 1.0-alpha-7
     */
    @Override
    public void addArchivedFileSet( @Nonnull final File archiveFile, final String prefix, final String[] includes,
                                    final String[] excludes )
        throws ArchiverException
    {
        addArchivedFileSet(
            archivedFileSet( archiveFile ).prefixed( prefix ).includeExclude( includes, excludes ).includeEmptyDirs(
                includeEmptyDirs ) );
    }

    /**
     * @since 1.0-alpha-7
     */
    @Override
    public void addArchivedFileSet( @Nonnull final File archiveFile, final String prefix )
        throws ArchiverException
    {
        addArchivedFileSet( archivedFileSet( archiveFile ).prefixed( prefix ).includeEmptyDirs( includeEmptyDirs ) );
    }

    /**
     * @since 1.0-alpha-7
     */
    @Override
    public void addArchivedFileSet( @Nonnull final File archiveFile, final String[] includes, final String[] excludes )
        throws ArchiverException
    {
        addArchivedFileSet(
            archivedFileSet( archiveFile ).includeExclude( includes, excludes ).includeEmptyDirs( includeEmptyDirs ) );
    }

    /**
     * @since 1.0-alpha-7
     */
    @Override
    public void addArchivedFileSet( @Nonnull final File archiveFile )
        throws ArchiverException
    {
        addArchivedFileSet( archivedFileSet( archiveFile ).includeEmptyDirs( includeEmptyDirs ) );
    }

    /**
     * Allows us to pull the ArchiverManager instance out of the container without causing a chicken-and-egg
     * instantiation/composition problem.
     */
    @Override
    public void contextualize( final Context context )
        throws ContextException
    {
        final PlexusContainer container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );

        try
        {
            archiverManager = (ArchiverManager) container.lookup( ArchiverManager.ROLE );
        }
        catch ( final ComponentLookupException e )
        {
            throw new ContextException( "Error retrieving ArchiverManager instance: " + e.getMessage(), e );
        }
    }

    @Override
    public boolean isForced()
    {
        return forced;
    }

    @Override
    public void setForced( final boolean forced )
    {
        this.forced = forced;
    }

    @Override
    public void addArchiveFinalizer( final ArchiveFinalizer finalizer )
    {
        if ( finalizers == null )
        {
            finalizers = new ArrayList<ArchiveFinalizer>();
        }

        finalizers.add( finalizer );
    }

    @Override
    public void setArchiveFinalizers( final List<ArchiveFinalizer> archiveFinalizers )
    {
        finalizers = archiveFinalizers;
    }

    @Override
    public void setDotFileDirectory( final File dotFileDirectory )
    {
        this.dotFileDirectory = dotFileDirectory;
    }

    protected boolean isUptodate()
        throws ArchiverException
    {
        final File zipFile = getDestFile();
        if ( !zipFile.exists() )
        {
            getLogger().debug( "isUp2date: false (Destination " + zipFile.getPath() + " not found.)" );
            return false; // File doesn't yet exist
        }
        final long destTimestamp = getFileLastModifiedTime(zipFile);

        final Iterator it = resources.iterator();
        if ( !it.hasNext() )
        {
            getLogger().debug( "isUp2date: false (No input files.)" );
            return false; // No timestamp to compare
        }

        while ( it.hasNext() )
        {
            final Object o = it.next();
            final long l;
            if ( o instanceof ArchiveEntry )
            {
                l = ( (ArchiveEntry) o ).getResource().getLastModified();
            }
            else if ( o instanceof AddedResourceCollection )
            {
                try
                {
                    l = ( (AddedResourceCollection) o ).resources.getLastModified();
                }
                catch ( final IOException e )
                {
                    throw new ArchiverException( e.getMessage(), e );
                }
            }
            else
            {
                throw new IllegalStateException( "Invalid object type: " + o.getClass().getName() );
            }
            if ( l == PlexusIoResource.UNKNOWN_MODIFICATION_DATE )
            {
                // Don't know what to do. Safe thing is to assume not up2date.
                getLogger().debug( "isUp2date: false (Resource with unknown modification date found.)" );
                return false;
            }
            if ( l > destTimestamp )
            {
                getLogger().debug( "isUp2date: false (Resource with newer modification date found.)" );
                return false;
            }
        }

        getLogger().debug( "isUp2date: true" );
        return true;
    }

    /**
     * Returns the last modified time in milliseconds of a file.
     * It avoids the bug where milliseconds precision is lost on File#lastModified (JDK-8177809) on JDK8 and Linux.
     * @param file The file where the last modified time will be returned for.
     * @return The last modified time in milliseconds of the file.
     * @throws ArchiverException In the case of an IOException, for example when the file does not exists.
     */
    private long getFileLastModifiedTime( File file )
            throws ArchiverException
    {
        try
        {
            return Files.getLastModifiedTime( file.toPath() ).toMillis();
        }
        catch ( IOException e )
        {
            throw new ArchiverException( e.getMessage(), e );
        }
    }

    protected boolean checkForced()
        throws ArchiverException
    {
        if ( !isForced() && isSupportingForced() && isUptodate() )
        {
            getLogger().debug( "Archive " + getDestFile() + " is uptodate." );
            return false;
        }
        return true;
    }

    @Override
    public boolean isSupportingForced()
    {
        return false;
    }

    protected void runArchiveFinalizers()
        throws ArchiverException
    {
        if ( finalizers != null )
        {
            for ( final ArchiveFinalizer finalizer : finalizers )
            {
                finalizer.finalizeArchiveCreation( this );
            }
        }
    }

    @Override
    public final void createArchive()
        throws ArchiverException, IOException
    {
        validate();
        try
        {
            try
            {
                if ( dotFileDirectory != null )
                {
                    addArchiveFinalizer( new DotDirectiveArchiveFinalizer( dotFileDirectory ) );
                }

                runArchiveFinalizers();

                execute();
            }
            finally
            {
                close();
            }
        }
        catch ( final IOException e )
        {
            String msg = "Problem creating " + getArchiveType() + ": " + e.getMessage();

            final StringBuffer revertBuffer = new StringBuffer();
            if ( !revert( revertBuffer ) )
            {
                msg += revertBuffer.toString();
            }

            throw new ArchiverException( msg, e );
        }
        finally
        {
            cleanUp();
        }

        postCreateArchive();
    }

    protected boolean hasVirtualFiles()
    {
        if ( finalizers != null )
        {
            for ( final ArchiveFinalizer finalizer : finalizers )
            {
                final List virtualFiles = finalizer.getVirtualFiles();

                if ( ( virtualFiles != null ) && !virtualFiles.isEmpty() )
                {
                    return true;
                }
            }
        }
        return false;
    }

    protected boolean revert( final StringBuffer messageBuffer )
    {
        return true;
    }

    protected void validate()
        throws ArchiverException, IOException
    {
    }

    /**
     * This method is called after the archive creation
     * completes successfully (no exceptions are thrown).
     *
     * Subclasses may override this method in order to
     * augment or validate the archive after it is
     * created.
     *
     * @since 3.6
     */
    protected void postCreateArchive()
        throws ArchiverException, IOException
    {
    }

    protected abstract String getArchiveType();

    private void addCloseable( Object maybeCloseable )
    {
        if ( maybeCloseable instanceof Closeable )
        {
            closeables.add( (Closeable) maybeCloseable );
        }

    }

    private void closeIterators()
    {
        for ( Closeable closeable : closeables )
        {
            closeQuietlyIfCloseable( closeable );
        }

    }

    protected abstract void close()
        throws IOException;

    protected void cleanUp()
        throws IOException
    {
        closeIterators();

        for ( Object resource : resources )
        {
            if ( resource instanceof PlexusIoProxyResourceCollection )
            {
                resource = ( (PlexusIoProxyResourceCollection) resource ).getSrc();
            }

            closeIfCloseable( resource );
        }
        resources.clear();
    }

    protected abstract void execute()
        throws ArchiverException, IOException;

    /**
     * @since 1.1
     */
    @Override
    public boolean isUseJvmChmod()
    {
        return useJvmChmod;
    }

    /**
     * @since 1.1
     */
    @Override
    public void setUseJvmChmod( final boolean useJvmChmod )
    {
        this.useJvmChmod = useJvmChmod;
    }

    /**
     * @since 1.1
     */
    @Override
    public boolean isIgnorePermissions()
    {
        return ignorePermissions;
    }

    /**
     * @since 1.1
     */
    @Override
    public void setIgnorePermissions( final boolean ignorePermissions )
    {
        this.ignorePermissions = ignorePermissions;
    }

    @Override
    public void setLastModifiedDate( Date lastModifiedDate )
    {
        this.lastModifiedDate = lastModifiedDate;
    }

    @Override
    public Date getLastModifiedDate()
    {
        return lastModifiedDate;
    }

    @Override
    public void setFilenameComparator( Comparator<String> filenameComparator )
    {
        this.filenameComparator = filenameComparator;
    }

    public Comparator<String> getFilenameComparator()
    {
        return filenameComparator;
    }

    @Override
    public void setOverrideUid( int uid )
    {
        overrideUid = uid;
    }

    @Override
    public void setOverrideUserName( String userName )
    {
        overrideUserName = userName;
    }

    @Override
    public int getOverrideUid()
    {
        return overrideUid;
    }

    @Override
    public String getOverrideUserName()
    {
        return overrideUserName;
    }

    @Override
    public void setOverrideGid( int gid )
    {
        overrideGid = gid;
    }

    @Override
    public void setOverrideGroupName( String groupName )
    {
        overrideGroupName = groupName;
    }

    @Override
    public int getOverrideGid()
    {
        return overrideGid;
    }

    @Override
    public String getOverrideGroupName()
    {
        return overrideGroupName;
    }

    @Override
    public void configureReproducible( Date lastModifiedDate )
    {
        // 1. force last modified date
        setLastModifiedDate( normalizeLastModifiedDate( lastModifiedDate ) );

        // 2. sort filenames in each directory when scanning filesystem
        setFilenameComparator( new Comparator<String>()
        {
            @Override
            public int compare( String s1, String s2 )
            {
                return s1.compareTo( s2 );
            }
        } );

        // 3. ignore file/directory mode from filesystem, since they may vary based on local user umask
        // notice: this overrides execute bit on Unix (that is already ignored on Windows)
        setFileMode( Archiver.DEFAULT_FILE_MODE );
        setDirectoryMode( Archiver.DEFAULT_DIR_MODE );

        // 4. ignore uid/gid from filesystem (for tar)
        setOverrideUid( 0 );
        setOverrideUserName( "root" ); // is it possible to avoid this, like "tar --numeric-owner"?
        setOverrideGid( 0 );
        setOverrideGroupName( "root" );
    }

    /**
     * Normalize last modified time value to get reproducible archive entries, based on
     * archive binary format (tar uses UTC timestamp but zip uses local time then requires
     * tweaks to make the value reproducible whatever the current timezone is).
     *
     * @param lastModifiedDate 
     * @return
     */
    protected Date normalizeLastModifiedDate( Date lastModifiedDate )
    {
        return lastModifiedDate;
    }
}


<|start_filename|>src/main/java/org/codehaus/plexus/archiver/xz/PlexusIoXZResourceCollection.java<|end_filename|>
/*
 * Copyright 2016 Codehaus.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.codehaus.plexus.archiver.xz;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;

import org.codehaus.plexus.archiver.util.Streams;
import org.codehaus.plexus.components.io.attributes.FileAttributes;
import org.codehaus.plexus.components.io.attributes.PlexusIoResourceAttributes;
import org.codehaus.plexus.components.io.resources.PlexusIoCompressedFileResourceCollection;

/**
 * @author lore
 * @since 3.3
 */
public class PlexusIoXZResourceCollection extends PlexusIoCompressedFileResourceCollection
{

    @Override
    protected PlexusIoResourceAttributes getAttributes( File file ) throws IOException
    {
        return new FileAttributes( file, new HashMap<Integer, String>(), new HashMap<Integer, String>() );
    }

    @Override
    protected String getDefaultExtension()
    {
        return ".xz";
    }

    @Override
    protected InputStream getInputStream( File file ) throws IOException
    {
        return XZUnArchiver.getXZInputStream( Streams.fileInputStream( file ) );
    }

}


<|start_filename|>src/main/java/org/codehaus/plexus/archiver/util/ResourceUtils.java<|end_filename|>
package org.codehaus.plexus.archiver.util;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;

import org.codehaus.plexus.components.io.functions.FileSupplier;
import org.codehaus.plexus.components.io.resources.PlexusIoResource;
import org.codehaus.plexus.util.IOUtil;

/**
 * Utility class for work with {@link PlexusIoResource} instances.
 */
public class ResourceUtils
{

    /**
     * Private constructor, to prevent accidental implementation.
     */
    private ResourceUtils()
    {
        // Does nothing
    }

    /**
     * Queries, whether the given source is up-to-date relative to
     * the given destination.
     */
    public static boolean isUptodate( PlexusIoResource source, File destination )
    {
        return isUptodate( source, destination.lastModified() );
    }

    /**
     * Queries, whether the given source is up-to-date relative to
     * the given modification date.
     */
    public static boolean isUptodate( PlexusIoResource source, long destinationDate )
    {
        final long s = source.getLastModified();
        if ( s == PlexusIoResource.UNKNOWN_MODIFICATION_DATE )
        {
            return false;
        }

        if ( destinationDate == 0 )
        {
            return false;
        }

        return destinationDate > s;
    }

    /**
     * Queries, whether the given source is up-to-date relative to
     * the given modification date.
     */
    public static boolean isUptodate( long sourceDate, long destinationDate )
    {
        if ( sourceDate == PlexusIoResource.UNKNOWN_MODIFICATION_DATE )
        {
            return false;
        }

        if ( destinationDate == 0 )
        {
            return false;
        }

        return destinationDate > sourceDate;
    }

    /**
     * Copies the sources contents to the given destination file.
     */
    public static void copyFile( PlexusIoResource in, File outFile )
        throws IOException
    {
        try ( InputStream input = in.getContents(); OutputStream output = Files.newOutputStream( outFile.toPath() ) )
        {
            IOUtil.copy( input, output );
        }
    }

    /**
     * Copies the sources contents to the given destination file.
     */
    public static void copyFile( InputStream input, File outFile )
        throws IOException
    {
        OutputStream output = null;
        try
        {
            output = Files.newOutputStream( outFile.toPath() );
            IOUtil.copy( input, output );
            output.close();
            output = null;
            input.close();
            input = null;
        }
        finally
        {
            IOUtil.close( input );
            IOUtil.close( output );
        }
    }

    /**
     * Checks, whether the resource and the file are identical.
     */
    public static boolean isSame( PlexusIoResource resource, File file )
    {
        if ( resource instanceof FileSupplier )
        {
            File resourceFile = ( (FileSupplier) resource ).getFile();
            return file.equals( resourceFile );
        }
        return false;
    }

    /**
     * Checks, whether the resource and the file are identical.
     * Uses {@link File#getCanonicalFile()} for comparison, which is much
     * slower than comparing the files.
     */
    public static boolean isCanonicalizedSame( PlexusIoResource resource, File file )
        throws IOException
    {
        if ( resource instanceof FileSupplier )
        {
            File resourceFile = ( (FileSupplier) resource ).getFile();
            return file.getCanonicalFile().equals( resourceFile.getCanonicalFile() );
        }
        return false;
    }

}


<|start_filename|>src/test/java/org/codehaus/plexus/archiver/zip/ZipUnArchiverTest.java<|end_filename|>
package org.codehaus.plexus.archiver.zip;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import org.codehaus.plexus.PlexusTestCase;
import org.codehaus.plexus.archiver.Archiver;
import org.codehaus.plexus.archiver.UnArchiver;
import org.codehaus.plexus.components.io.fileselectors.FileInfo;
import org.codehaus.plexus.components.io.fileselectors.FileSelector;
import org.codehaus.plexus.components.io.fileselectors.IncludeExcludeFileSelector;
import org.codehaus.plexus.util.FileUtils;

/**
 * @author <NAME>
 */
public class ZipUnArchiverTest
    extends PlexusTestCase
{

    public void testExtractingZipPreservesExecutableFlag()
        throws Exception
    {

        String s = "target/zip-unarchiver-tests";
        File testZip = new File( getBasedir(), "src/test/jars/test.zip" );
        File outputDirectory = new File( getBasedir(), s );

        FileUtils.deleteDirectory( outputDirectory );

        ZipUnArchiver zu = getZipUnArchiver( testZip );
        zu.extract( "", outputDirectory );
        File testScript = new File( outputDirectory, "test.sh" );

        final Method canExecute;
        try
        {
            canExecute = File.class.getMethod( "canExecute" );
            canExecute.invoke( testScript );
            assertTrue( (Boolean) canExecute.invoke( testScript ) );
        }
        catch ( NoSuchMethodException ignore )
        {
        }
    }

    public void testZeroFileModeInZip()
        throws Exception
    {

        String s = "target/zip-unarchiver-filemode-tests";
        File testZip = new File( getBasedir(), "src/test/resources/zeroFileMode/foobar.zip" );
        File outputDirectory = new File( getBasedir(), s );

        FileUtils.deleteDirectory( outputDirectory );

        ZipUnArchiver zu = getZipUnArchiver( testZip );
        zu.setIgnorePermissions( false );
        zu.extract( "", outputDirectory );

        File testScript = new File( outputDirectory, "foo.txt" );

        final Method canRead;
        try
        {
            canRead = File.class.getMethod( "canRead" );
            canRead.invoke( testScript );
            assertTrue( (Boolean) canRead.invoke( testScript ) );
        }
        catch ( NoSuchMethodException ignore )
        {
        }
    }

    public void testUnarchiveUtf8()
        throws Exception
    {
        File dest = new File( "target/output/unzip/utf8" );
        dest.mkdirs();

        final File zipFile = new File( "target/output/unzip/utf8-default.zip" );
        final ZipArchiver zipArchiver = getZipArchiver( zipFile );
        zipArchiver.addDirectory( new File( "src/test/resources/miscUtf8" ) );
        zipArchiver.createArchive();
        final ZipUnArchiver unarchiver = getZipUnArchiver( zipFile );
        unarchiver.setDestFile( dest );
        unarchiver.extract();
        assertTrue( new File( dest, "aPi\u00F1ata.txt" ).exists() );
        assertTrue( new File( dest, "an\u00FCmlaut.txt" ).exists() );
        assertTrue( new File( dest, "\u20acuro.txt" ).exists() );
    }

    public void testUnarchiveUnicodePathExtra()
        throws Exception
    {
        File dest = new File( "target/output/unzip/unicodePathExtra" );
        dest.mkdirs();
        for ( String name : dest.list() )
        {
            new File( dest, name ).delete();
        }
        assertEquals( 0, dest.list().length );

        final ZipUnArchiver unarchiver = getZipUnArchiver( new File( "src/test/resources/unicodePathExtra/efsclear.zip" ) );
        unarchiver.setDestFile( dest );
        unarchiver.extract();
        // a Unicode Path extra field should only be used when its CRC matches the header file name
        assertEquals( "should use good extra fields but not bad ones",
                new HashSet<>( Arrays.asList( "nameonly-name", "goodextra-extra", "badextra-name" ) ),
                new HashSet<>( Arrays.asList( dest.list() ) ) );
    }

    public void testUnarchiveUnicodePathExtraSelector()
        throws Exception
    {
        File dest = new File( "target/output/unzip/unicodePathExtraSelector" );
        dest.mkdirs();
        for ( String name : dest.list() )
        {
            new File( dest, name ).delete();
        }
        assertEquals( 0, dest.list().length );

        class CollectingSelector implements FileSelector
        {
            public Set<String> collection = new HashSet<>();
            @Override
            public boolean isSelected( FileInfo fileInfo ) throws IOException
            {
                collection.add( fileInfo.getName() );
                return false;
            }
        }
        CollectingSelector selector = new CollectingSelector();

        final ZipUnArchiver unarchiver = getZipUnArchiver( new File( "src/test/resources/unicodePathExtra/efsclear.zip" ) );
        unarchiver.setDestFile( dest );
        unarchiver.setFileSelectors( new FileSelector[] { selector } );
        unarchiver.extract();

        assertEquals( "should not extract anything", 0, dest.list().length );
        // a Unicode Path extra field should only be used when its CRC matches the header file name
        assertEquals( "should use good extra fields but not bad ones",
                new HashSet<>( Arrays.asList( "nameonly-name", "goodextra-extra", "badextra-name" ) ),
                selector.collection );
    }

    private void runUnarchiver( String path, FileSelector[] selectors, boolean[] results )
        throws Exception
    {
        String s = "target/zip-unarchiver-tests";

        File testJar = new File( getBasedir(), "src/test/jars/test.jar" );

        File outputDirectory = new File( getBasedir(), s );

        ZipUnArchiver zu = getZipUnArchiver( testJar );
        zu.setFileSelectors( selectors );

        FileUtils.deleteDirectory( outputDirectory );

        zu.extract( path, outputDirectory );

        File f0 = new File( getBasedir(), s + "/resources/artifactId/test.properties" );

        assertEquals( results[0], f0.exists() );

        File f1 = new File( getBasedir(), s + "/resources/artifactId/directory/test.properties" );

        assertEquals( results[1], f1.exists() );

        File f2 = new File( getBasedir(), s + "/META-INF/MANIFEST.MF" );

        assertEquals( results[2], f2.exists() );
    }

    private ZipUnArchiver getZipUnArchiver( File testJar ) throws Exception
    {
        ZipUnArchiver zu = (ZipUnArchiver) lookup( UnArchiver.ROLE, "zip" );
        zu.setSourceFile( testJar );
        return zu;
    }

    public void testExtractingADirectoryFromAJarFile()
        throws Exception
    {
        runUnarchiver( "resources/artifactId", null,
                       new boolean[]
                       {
                           true, true, false
                       } );
        runUnarchiver( "", null,
                       new boolean[]
                       {
                           true, true, true
                       } );
    }

    public void testSelectors()
        throws Exception
    {
        IncludeExcludeFileSelector fileSelector = new IncludeExcludeFileSelector();
        runUnarchiver( "", new FileSelector[]
                   {
                       fileSelector
        },
                       new boolean[]
                       {
                           true, true, true
                       } );
        fileSelector.setExcludes( new String[]
        {
            "**/test.properties"
        } );
        runUnarchiver( "", new FileSelector[]
                   {
                       fileSelector
        },
                       new boolean[]
                       {
                           false, false, true
                       } );
        fileSelector.setIncludes( new String[]
        {
            "**/test.properties"
        } );
        fileSelector.setExcludes( null );
        runUnarchiver( "", new FileSelector[]
                   {
                       fileSelector
        },
                       new boolean[]
                       {
                           true, true, false
                       } );
        fileSelector.setExcludes( new String[]
        {
            "resources/artifactId/directory/test.properties"
        } );
        runUnarchiver( "", new FileSelector[]
                   {
                       fileSelector
        },
                       new boolean[]
                       {
                           true, false, false
                       } );
    }

    public void testExtractingZipWithEntryOutsideDestDirThrowsException()
            throws Exception
    {
        Exception ex = null;
        String s = "target/zip-unarchiver-slip-tests";
        File testZip = new File( getBasedir(), "src/test/zips/zip-slip.zip" );
        File outputDirectory = new File( getBasedir(), s );

        FileUtils.deleteDirectory( outputDirectory );

        try
        {
            ZipUnArchiver zu = getZipUnArchiver( testZip );
            zu.extract( "", outputDirectory );
        }
        catch ( Exception e )
        {
            ex = e;
        }

        assertNotNull( ex );
        assertTrue( ex.getMessage().startsWith( "Entry is outside of the target directory" ) );
    }

    public void testZipOutputSizeException()
        throws Exception
    {
        Exception ex = null;
        String s = "target/zip-size-tests";
        File testZip = new File( getBasedir(), "src/test/jars/test.zip" );
        File outputDirectory = new File( getBasedir(), s );

        FileUtils.deleteDirectory( outputDirectory );

        try
        {
            ZipUnArchiver zu = getZipUnArchiver( testZip );
            zu.setMaxOutputSize( 10L );
            zu.extract( "", outputDirectory );
        }
        catch ( Exception e )
        {
            ex = e;
        }

        assertNotNull( ex );
        assertTrue( ex.getMessage().startsWith( "Maximum output size limit reached" ) );
    }

    public void testZipMaxOutputSizeEqualToExtractedFileSize()
        throws Exception
    {
        long extractedFileSize = 11L;
        String s = "target/zip-size-tests";
        File testZip = new File( getBasedir(), "src/test/jars/test.zip" );
        File outputDirectory = new File( getBasedir(), s );

        FileUtils.deleteDirectory( outputDirectory );

        ZipUnArchiver zu = getZipUnArchiver( testZip );
        zu.setMaxOutputSize( extractedFileSize );
        zu.extract( "", outputDirectory );

        File extractedFile = new File( outputDirectory, "test.sh" );
        assertEquals( extractedFileSize, extractedFile.length() );
    }

    private ZipArchiver getZipArchiver()
    {
        try
        {
            return (ZipArchiver) lookup( Archiver.ROLE, "zip" );
        }
        catch ( Exception e )
        {
            throw new RuntimeException( e );
        }
    }

    private ZipArchiver getZipArchiver( File destFile )
    {
        final ZipArchiver zipArchiver = getZipArchiver();
        zipArchiver.setDestFile( destFile );
        return zipArchiver;
    }

}


<|start_filename|>src/main/java/org/codehaus/plexus/archiver/zip/AddedDirs.java<|end_filename|>
/**
 *
 * Copyright 2015 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.codehaus.plexus.archiver.zip;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;

/**
 * A list of directories that have been added to an archive.
 */
public class AddedDirs
{

    private final Set<String> addedDirs = new HashSet<String>();

    /**
     * @deprecated use {@link #asStringDeque(String)} instead.
     */
    @Deprecated
    public Stack<String> asStringStack( String entry )
    {
        Stack<String> directories = new Stack<>();

        // Don't include the last entry itself if it's
        // a dir; it will be added on its own.
        int slashPos = entry.length() - ( entry.endsWith( "/" ) ? 1 : 0 );

        while ( ( slashPos = entry.lastIndexOf( '/', slashPos - 1 ) ) != -1 )
        {
            String dir = entry.substring( 0, slashPos + 1 );

            if ( addedDirs.contains( dir ) )
            {
                break;
            }

            directories.push( dir );
        }
        return directories;
    }

    public Deque<String> asStringDeque( String entry )
    {
        Deque<String> directories = new ArrayDeque<>();

        // Don't include the last entry itself if it's
        // a dir; it will be added on its own.
        int slashPos = entry.length() - ( entry.endsWith( "/" ) ? 1 : 0 );

        while ( ( slashPos = entry.lastIndexOf( '/', slashPos - 1 ) ) != -1 )
        {
            String dir = entry.substring( 0, slashPos + 1 );

            if ( addedDirs.contains( dir ) )
            {
                break;
            }

            directories.push( dir );
        }
        return directories;
    }

    public void clear()
    {
        addedDirs.clear();
    }

    /**
     * Adds the path to this list.
     *
     * @param vPath The path to add.
     *
     * @return true if the path was already present, false if it has been added.
     */
    public boolean update( String vPath )
    {
        return !addedDirs.add( vPath );
    }

    public Set<String> allAddedDirs()
    {
        return new HashSet<String>( addedDirs );
    }

}


<|start_filename|>src/test/java/org/codehaus/plexus/archiver/zip/PlexusIoZipFileResourceCollectionTest.java<|end_filename|>
package org.codehaus.plexus.archiver.zip;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.codehaus.plexus.PlexusTestCase;
import org.codehaus.plexus.components.io.functions.SymlinkDestinationSupplier;
import org.codehaus.plexus.components.io.resources.PlexusIoResource;
import org.codehaus.plexus.components.io.resources.PlexusIoURLResource;

public class PlexusIoZipFileResourceCollectionTest
    extends PlexusTestCase
{

    public void testNamelessRootFolder()
        throws Exception
    {
        PlexusIoZipFileResourceCollection resourceCollection = new PlexusIoZipFileResourceCollection();
        resourceCollection.setFile( getTestFile( "src/test/jars/namelessrootfolder.jar" ) );
        Iterator iterator = resourceCollection.getResources();
        PlexusIoURLResource entry = (PlexusIoURLResource) iterator.next();
        assertEquals( "/dummy.txt", entry.getName() );
        final URL url = entry.getURL();
        BufferedReader d = new BufferedReader( new InputStreamReader( entry.getContents() ) );
        assertEquals( "dummy content", d.readLine() );
    }

    public void testDescriptionForError()
        throws Exception
    {
        PlexusIoZipFileResourceCollection resourceCollection = new PlexusIoZipFileResourceCollection();
        resourceCollection.setFile( getTestFile( "src/test/jars/namelessrootfolder.jar" ) );
        Iterator<PlexusIoResource> iterator = resourceCollection.getResources();
        PlexusIoURLResource entry = (PlexusIoURLResource) iterator.next();
        final URL url = entry.getURL();
        String descriptionForError = entry.getDescriptionForError( url );
        assertTrue( descriptionForError.endsWith( "namelessrootfolder.jar!//dummy.txt" ) );
    }

    public void testFilesWithIllegalHtmlChars()
        throws Exception
    {
        File testZip = new File( getBasedir(), "src/test/resources/bogusManifest.zip" );
        PlexusIoZipFileResourceCollection prc = new PlexusIoZipFileResourceCollection();
        prc.setFile( testZip );
        final Iterator<PlexusIoResource> entries = prc.getEntries();
        while ( entries.hasNext() )
        {
            final PlexusIoResource next = entries.next();
            if ( next.getName().endsWith( "MANIFEST.MF" ) )
            {
                final InputStream contents1 = next.getContents();
                final String manifest = IOUtils.toString( contents1, "UTF-8" );
                assertTrue( manifest.contains( "bogs=fus" ) );
                contents1.close();
            }

        }
    }

    public void testFilesThatAreNotThere()
        throws Exception
    {
        File testZip = new File( getBasedir(), "src/test/resources/archiveWithIllegalHtmlFileName.zip" );
        Set<String> seen = new HashSet<String>();
        seen.add( "AFileThatNeedsHtmlEsc%3F&gt" );
        seen.add( "Afile&lt;Yo&gt;.txt" );
        seen.add( "File With Space.txt" );
        seen.add( "FileWith%.txt" );
        PlexusIoZipFileResourceCollection prc = new PlexusIoZipFileResourceCollection();
        prc.setFile( testZip );
        final Iterator<PlexusIoResource> entries = prc.getEntries();
        while ( entries.hasNext() )
        {
            final PlexusIoResource next = entries.next();
            assertTrue( next.getName() + "was not present", seen.remove( next.getName() ) );
            final URL url = next.getURL();
            final InputStream contents = next.getContents();
            contents.close();
        }
    }

    public void testSymlinkEntries()
        throws Exception
    {
        File testZip = new File( getBasedir(), "src/test/resources/symlinks/symlinks.zip" );
        Map<String, String> symLinks = new HashMap<String, String>();
        symLinks.put( "symDir", "targetDir/" );
        symLinks.put( "symLinkToDirOnTheOutside", "../dirOnTheOutside/" );
        symLinks.put( "symLinkToTheOutside", "../onTheOutside.txt" );
        symLinks.put( "symR", "fileR.txt" );
        symLinks.put( "symW", "fileW.txt" );
        symLinks.put( "symX", "fileX.txt" );
        PlexusIoZipFileResourceCollection prc = new PlexusIoZipFileResourceCollection();
        prc.setFile( testZip );
        final Iterator<PlexusIoResource> entries = prc.getEntries();
        while ( entries.hasNext() )
        {
            final PlexusIoResource next = entries.next();
            String symLinkTarget = symLinks.remove( next.getName() );
            if ( symLinkTarget != null )
            {
                assertTrue( next.getName() + " must be symlink", next.isSymbolicLink() );
                assertTrue( next instanceof SymlinkDestinationSupplier );
                assertEquals( symLinkTarget,
                              ( (SymlinkDestinationSupplier) next ).getSymlinkDestination() );
            }
            else
            {
                assertFalse( next.getName() + " must not be symlink", next.isSymbolicLink() );
            }
        }

        assertTrue( symLinks.isEmpty() );
    }

    public void testUnarchiveUnicodePathExtra()
        throws Exception
    {
        PlexusIoZipFileResourceCollection prc = new PlexusIoZipFileResourceCollection();
        prc.setFile( getTestFile( "src/test/resources/unicodePathExtra/efsclear.zip" ) );
        Set<String> names = new HashSet<>();
        final Iterator<PlexusIoResource> entries = prc.getEntries();
        while ( entries.hasNext() )
        {
            final PlexusIoResource next = entries.next();
            names.add(next.getName());
        }
        // a Unicode Path extra field should only be used when its CRC matches the header file name
        assertEquals( "should use good extra fields but not bad ones",
                new HashSet<>( Arrays.asList( "nameonly-name", "goodextra-extra", "badextra-name" ) ),
                names );
    }

}


<|start_filename|>src/main/java/org/codehaus/plexus/archiver/bzip2/PlexusIoBzip2ResourceCollection.java<|end_filename|>
package org.codehaus.plexus.archiver.bzip2;

import static org.codehaus.plexus.archiver.util.Streams.fileInputStream;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import javax.annotation.Nonnull;
import javax.annotation.WillNotClose;

import org.codehaus.plexus.components.io.attributes.FileAttributes;
import org.codehaus.plexus.components.io.attributes.PlexusIoResourceAttributes;
import org.codehaus.plexus.components.io.resources.PlexusIoCompressedFileResourceCollection;
import org.codehaus.plexus.components.io.resources.PlexusIoResourceCollection;

/**
 * Implementation of {@link PlexusIoResourceCollection} for
 * bzip2 compressed files.
 */
public class PlexusIoBzip2ResourceCollection
    extends PlexusIoCompressedFileResourceCollection
{

    @Nonnull
    @Override
    protected @WillNotClose
    InputStream getInputStream( File file )
        throws IOException
    {
        return BZip2UnArchiver.getBZip2InputStream( fileInputStream( file ) );
    }

    @Override protected PlexusIoResourceAttributes getAttributes( File file ) throws IOException
    {
        return new FileAttributes( file, new HashMap<Integer, String>(), new HashMap<Integer, String>() );
    }

    @Override
    protected String getDefaultExtension()
    {
        return ".bz2";
    }

}


<|start_filename|>src/main/java/org/codehaus/plexus/archiver/snappy/PlexusIoSnappyResourceCollection.java<|end_filename|>
package org.codehaus.plexus.archiver.snappy;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;

import javax.annotation.Nonnull;
import javax.annotation.WillNotClose;

import org.codehaus.plexus.archiver.util.Streams;
import org.codehaus.plexus.components.io.attributes.FileAttributes;
import org.codehaus.plexus.components.io.attributes.PlexusIoResourceAttributes;
import org.codehaus.plexus.components.io.resources.PlexusIoCompressedFileResourceCollection;

/**
 * Implementation of {@link org.codehaus.plexus.components.io.resources.PlexusIoResourceCollection} for
 * snappy compressed files.
 */
public class PlexusIoSnappyResourceCollection
    extends PlexusIoCompressedFileResourceCollection
{

    @Nonnull
    @Override
    protected @WillNotClose
    InputStream getInputStream( File file )
        throws IOException
    {
        return SnappyUnArchiver.getSnappyInputStream( Streams.fileInputStream( file ) );
    }

    @Override protected PlexusIoResourceAttributes getAttributes( File file ) throws IOException
    {
        return new FileAttributes( file, new HashMap<Integer, String>(), new HashMap<Integer, String>() );
    }

    @Override
    protected String getDefaultExtension()
    {
        return ".snappy";
    }

}


<|start_filename|>src/main/java/org/codehaus/plexus/archiver/gzip/PlexusIoGzipResourceCollection.java<|end_filename|>
package org.codehaus.plexus.archiver.gzip;

import static org.codehaus.plexus.archiver.util.Streams.bufferedInputStream;
import static org.codehaus.plexus.archiver.util.Streams.fileInputStream;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.zip.GZIPInputStream;

import javax.annotation.Nonnull;

import org.codehaus.plexus.components.io.attributes.FileAttributes;
import org.codehaus.plexus.components.io.attributes.PlexusIoResourceAttributes;
import org.codehaus.plexus.components.io.resources.PlexusIoCompressedFileResourceCollection;

/**
 * Abstract base class for compressed files, aka singleton
 * resource collections.
 */
public class PlexusIoGzipResourceCollection
    extends PlexusIoCompressedFileResourceCollection
{

    @Override
    protected String getDefaultExtension()
    {
        return ".gz";
    }

    @Nonnull
    @Override
    protected InputStream getInputStream( File file )
        throws IOException
    {
        return bufferedInputStream( new GZIPInputStream( fileInputStream( file ) ) );
    }

    @Override
    protected PlexusIoResourceAttributes getAttributes( File file )
        throws IOException
    {
        return new FileAttributes( file, new HashMap<Integer, String>(), new HashMap<Integer, String>() );
    }

}


<|start_filename|>src/test/java/org/codehaus/plexus/archiver/tar/TarFileAttributesTest.java<|end_filename|>
package org.codehaus.plexus.archiver.tar;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import org.codehaus.plexus.PlexusTestCase;
import org.codehaus.plexus.archiver.Archiver;
import org.codehaus.plexus.archiver.UnArchiver;
import org.codehaus.plexus.archiver.util.DefaultArchivedFileSet;
import org.codehaus.plexus.components.io.attributes.AttributeUtils;
import org.codehaus.plexus.components.io.attributes.PlexusIoResourceAttributeUtils;
import org.codehaus.plexus.components.io.attributes.PlexusIoResourceAttributes;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.Os;

@SuppressWarnings( "ResultOfMethodCallIgnored" )
public class TarFileAttributesTest
    extends PlexusTestCase
{

    private final List<File> toDelete = new ArrayList<File>();

    @Override
    public void setUp()
        throws Exception
    {
        super.setUp();

        System.out.println( "Octal 0660 is decimal " + 0660 );
        System.out.println( "Octal 0644 is decimal " + 0644 );
        System.out.println( "Octal 0440 is decimal " + 0440 );
    }

    @Override
    public void tearDown()
        throws Exception
    {
        super.tearDown();

        if ( !toDelete.isEmpty() )
        {
            for ( File f : toDelete )
            {
                System.out.println( "Deleting: " + f );
                if ( f.isDirectory() )
                {
                    try
                    {
                        FileUtils.deleteDirectory( f );
                    }
                    catch ( IOException e )
                    {
                        System.out.println( "Error deleting test directory: " + f );
                    }
                }
                else
                {
                    f.delete();
                    f.deleteOnExit();
                }
            }
        }
    }

    private void printTestHeader()
    {
        StackTraceElement e = new Throwable().getStackTrace()[1];
        System.out.println( "\n\nRunning: " + e.getMethodName() + "\n\n" );
    }

    public void testUseAttributesFromTarArchiveInputInTarArchiverOutput()
        throws Exception
    {
        printTestHeader();
        if ( checkForWindows() )
        {
            System.out.println( "This test cannot run on windows. Aborting." );
            return;
        }

        File tempFile = File.createTempFile( "tar-file-attributes.", ".tmp" );
        toDelete.add( tempFile );

        try ( Writer writer = Files.newBufferedWriter( tempFile.toPath(), StandardCharsets.UTF_8 ) )
        {
            writer.write( "This is a test file." );
        }

        AttributeUtils.chmod(tempFile, 0440);

        TarArchiver tarArchiver = getPosixCompliantTarArchiver();

        File tempTarFile = File.createTempFile( "tar-file.", ".tar" );
        toDelete.add( tempTarFile );

        tarArchiver.setDestFile( tempTarFile );
        tarArchiver.addFile( tempFile, tempFile.getName(), 0660 );

        tarArchiver.createArchive();

        TarArchiver tarArchiver2 = getPosixCompliantTarArchiver();

        File tempTarFile2 = File.createTempFile( "tar-file.", ".tar" );
        toDelete.add( tempTarFile2 );

        tarArchiver2.setDestFile( tempTarFile2 );

        DefaultArchivedFileSet afs = new DefaultArchivedFileSet( tempTarFile );

        System.out.println( "Adding tar archive to new archiver: " + tempTarFile );
        tarArchiver2.addArchivedFileSet( afs );

        tarArchiver2.createArchive();

        // Cut from here, and feed it into a new tar archiver...then unarchive THAT.
        TarUnArchiver tarUnArchiver = (TarUnArchiver) lookup( UnArchiver.ROLE, "tar" );

        File tempTarDir = File.createTempFile( "tar-test.", ".dir" );
        tempTarDir.delete();
        tempTarDir.mkdirs();

        toDelete.add( tempTarDir );

        tarUnArchiver.setDestDirectory( tempTarDir );
        tarUnArchiver.setSourceFile( tempTarFile2 );

        tarUnArchiver.extract();

        PlexusIoResourceAttributes fileAttributes =
            PlexusIoResourceAttributeUtils.getFileAttributes( new File( tempTarDir, tempFile.getName() ) );

        assertEquals( 0660, fileAttributes.getOctalMode() );

    }

    public void testUseDetectedFileAttributes()
        throws Exception
    {
        printTestHeader();
        if ( checkForWindows() )
        {
            System.out.println( "This test cannot run on windows. Aborting." );
            return;
        }

        File tempFile = File.createTempFile( "tar-file-attributes.", ".tmp" );
        toDelete.add( tempFile );

        try ( Writer writer = Files.newBufferedWriter( tempFile.toPath(), StandardCharsets.UTF_8 ) )
        {
            writer.write( "This is a test file." );
        }

        AttributeUtils.chmod(tempFile, 0440);

        PlexusIoResourceAttributes fileAttributes = PlexusIoResourceAttributeUtils.getFileAttributes( tempFile );

        assertEquals( 0440, fileAttributes.getOctalMode() );

        TarArchiver tarArchiver = getPosixCompliantTarArchiver();

        File tempTarFile = File.createTempFile( "tar-file.", ".tar" );
        toDelete.add( tempTarFile );

        tarArchiver.setDestFile( tempTarFile );
        tarArchiver.addFile( tempFile, tempFile.getName() );

        tarArchiver.createArchive();

        TarUnArchiver tarUnArchiver = (TarUnArchiver) lookup( UnArchiver.ROLE, "tar" );

        File tempTarDir = File.createTempFile( "tar-test.", ".dir" );
        tempTarDir.delete();
        tempTarDir.mkdirs();

        toDelete.add( tempTarDir );

        tarUnArchiver.setDestDirectory( tempTarDir );
        tarUnArchiver.setSourceFile( tempTarFile );

        tarUnArchiver.extract();

        fileAttributes = PlexusIoResourceAttributeUtils.getFileAttributes( new File( tempTarDir, tempFile.getName() ) );

        assertEquals( 0440, fileAttributes.getOctalMode() );

    }

    private boolean checkForWindows()
    {
        return Os.isFamily( Os.FAMILY_WINDOWS );
    }

    public void testOverrideDetectedFileAttributes()
        throws Exception
    {
        printTestHeader();

        if ( checkForWindows() )
        {
            System.out.println( "This test cannot run on windows. Aborting." );
            return;
        }

        File tempFile = File.createTempFile( "tar-file-attributes.", ".tmp" );
        toDelete.add( tempFile );

        try ( Writer writer = Files.newBufferedWriter( tempFile.toPath(), StandardCharsets.UTF_8 ) )
        {
            writer.write( "This is a test file." );
        }

        AttributeUtils.chmod(tempFile, 0440);

        TarArchiver tarArchiver = getPosixCompliantTarArchiver();

        File tempTarFile = File.createTempFile( "tar-file.", ".tar" );
        toDelete.add( tempTarFile );

        tarArchiver.setDestFile( tempTarFile );
        tarArchiver.addFile( tempFile, tempFile.getName(), 0660 );

        tarArchiver.createArchive();

        TarUnArchiver tarUnArchiver = (TarUnArchiver) lookup( UnArchiver.ROLE, "tar" );

        File tempTarDir = File.createTempFile( "tar-test.", ".dir" );
        tempTarDir.delete();
        tempTarDir.mkdirs();

        toDelete.add( tempTarDir );

        tarUnArchiver.setDestDirectory( tempTarDir );
        tarUnArchiver.setSourceFile( tempTarFile );

        tarUnArchiver.extract();

        PlexusIoResourceAttributes fileAttributes =
            PlexusIoResourceAttributeUtils.getFileAttributes( new File( tempTarDir, tempFile.getName() ) );

        assertEquals( 0660, fileAttributes.getOctalMode() );

    }

    private TarArchiver getPosixCompliantTarArchiver() throws Exception
    {
        TarArchiver tarArchiver = (TarArchiver) lookup( Archiver.ROLE, "tar" );
        tarArchiver.setLongfile( TarLongFileMode.posix );
        return tarArchiver;
    }

    public void testOverrideDetectedFileAttributesUsingFileMode()
        throws Exception
    {
        printTestHeader();
        if ( checkForWindows() )
        {
            System.out.println( "This test cannot run on windows. Aborting." );
            return;
        }

        File tempFile = File.createTempFile( "tar-file-attributes.", ".tmp" );
        toDelete.add( tempFile );

        try ( Writer writer = Files.newBufferedWriter( tempFile.toPath(), StandardCharsets.UTF_8 ) )
        {
            writer.write( "This is a test file." );
        }

        AttributeUtils.chmod(tempFile, 0440);

        TarArchiver tarArchiver = getPosixCompliantTarArchiver();

        File tempTarFile = File.createTempFile( "tar-file.", ".tar" );
        toDelete.add( tempTarFile );

        tarArchiver.setDestFile( tempTarFile );
        tarArchiver.setFileMode( 0660 );
        tarArchiver.addFile( tempFile, tempFile.getName() );

        tarArchiver.createArchive();

        TarUnArchiver tarUnArchiver = (TarUnArchiver) lookup( UnArchiver.ROLE, "tar" );

        File tempTarDir = File.createTempFile( "tar-test.", ".dir" );
        tempTarDir.delete();
        tempTarDir.mkdirs();

        toDelete.add( tempTarDir );

        tarUnArchiver.setDestDirectory( tempTarDir );
        tarUnArchiver.setSourceFile( tempTarFile );

        tarUnArchiver.extract();

        PlexusIoResourceAttributes fileAttributes =
            PlexusIoResourceAttributeUtils.getFileAttributes( new File( tempTarDir, tempFile.getName() ) );

        assertEquals( 0660, fileAttributes.getOctalMode() );

    }

}


<|start_filename|>src/main/java/org/codehaus/plexus/archiver/zip/OffloadingOutputStream.java<|end_filename|>
/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.codehaus.plexus.archiver.zip;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.SequenceInputStream;
import java.nio.file.Files;

import org.apache.commons.io.output.ThresholdingOutputStream;

/**
 * Offloads to disk when a given memory consumption has been reacehd
 */
class OffloadingOutputStream extends ThresholdingOutputStream
{

    // ----------------------------------------------------------- Data members

    /**
     * The output stream to which data will be written prior to the theshold
     * being reached.
     */
    private ByteArrayOutputStream memoryOutputStream;

    /**
     * The output stream to which data will be written at any given time. This
     * will always be one of <code>memoryOutputStream</code> or
     * <code>diskOutputStream</code>.
     */
    private OutputStream currentOutputStream;

    /**
     * The file to which output will be directed if the threshold is exceeded.
     */
    private File outputFile = null;

    /**
     * The temporary file prefix.
     */
    private final String prefix;

    /**
     * The temporary file suffix.
     */
    private final String suffix;

    /**
     * The directory to use for temporary files.
     */
    private final File directory;

    /**
     * True when close() has been called successfully.
     */
    private boolean closed = false;

    // ----------------------------------------------------------- Constructors

    /**
     * Constructs an instance of this class which will trigger an event at the
     * specified threshold, and save data to a temporary file beyond that point.
     *
     * @param threshold The number of bytes at which to trigger an event.
     * @param prefix Prefix to use for the temporary file.
     * @param suffix Suffix to use for the temporary file.
     * @param directory Temporary file directory.
     *
     * @since 1.4
     */
    public OffloadingOutputStream( int threshold, String prefix, String suffix, File directory )
    {
        this( threshold, null, prefix, suffix, directory );
        if ( prefix == null )
        {
            throw new IllegalArgumentException( "Temporary file prefix is missing" );
        }
    }

    /**
     * Constructs an instance of this class which will trigger an event at the
     * specified threshold, and save data either to a file beyond that point.
     *
     * @param threshold The number of bytes at which to trigger an event.
     * @param outputFile The file to which data is saved beyond the threshold.
     * @param prefix Prefix to use for the temporary file.
     * @param suffix Suffix to use for the temporary file.
     * @param directory Temporary file directory.
     */
    private OffloadingOutputStream( int threshold, File outputFile, String prefix, String suffix, File directory )
    {
        super( threshold );
        this.outputFile = outputFile;

        memoryOutputStream = new ByteArrayOutputStream( threshold / 10 );
        currentOutputStream = memoryOutputStream;
        this.prefix = prefix;
        this.suffix = suffix;
        this.directory = directory;
    }

    // --------------------------------------- ThresholdingOutputStream methods

    /**
     * Returns the current output stream. This may be memory based or disk
     * based, depending on the current state with respect to the threshold.
     *
     * @return The underlying output stream.
     *
     * @exception java.io.IOException if an error occurs.
     */
    @Override
    protected OutputStream getStream() throws IOException
    {
        return currentOutputStream;
    }

    /**
     * Switches the underlying output stream from a memory based stream to one
     * that is backed by disk. This is the point at which we realise that too
     * much data is being written to keep in memory, so we elect to switch to
     * disk-based storage.
     *
     * @exception java.io.IOException if an error occurs.
     */
    @Override
    protected void thresholdReached() throws IOException
    {
        if ( prefix != null )
        {
            outputFile = File.createTempFile( prefix, suffix, directory );
        }
        currentOutputStream = Files.newOutputStream( outputFile.toPath() );
    }

    public InputStream getInputStream() throws IOException
    {

        InputStream memoryAsInput = memoryOutputStream.toInputStream();
        if ( outputFile == null )
        {
            return memoryAsInput;
        }
        return new SequenceInputStream( memoryAsInput, Files.newInputStream( outputFile.toPath() ) );
    }

    // --------------------------------------------------------- Public methods

    /**
     * Returns the data for this output stream as an array of bytes, assuming
     * that the data has been retained in memory. If the data was written to
     * disk, this method returns <code>null</code>.
     *
     * @return The data for this output stream, or <code>null</code> if no such
     * data is available.
     */
    public byte[] getData()
    {
        if ( memoryOutputStream != null )
        {
            return memoryOutputStream.toByteArray();
        }
        return null;
    }

    /**
     * Returns either the output file specified in the constructor or
     * the temporary file created or null.
     * <p>
     * If the constructor specifying the file is used then it returns that
     * same output file, even when threshold has not been reached.
     * <p>
     * If constructor specifying a temporary file prefix/suffix is used
     * then the temporary file created once the threshold is reached is returned
     * If the threshold was not reached then <code>null</code> is returned.
     *
     * @return The file for this output stream, or <code>null</code> if no such
     * file exists.
     */
    public File getFile()
    {
        return outputFile;
    }

    /**
     * Closes underlying output stream, and mark this as closed
     *
     * @exception java.io.IOException if an error occurs.
     */
    @Override
    public void close() throws IOException
    {
        super.close();
        closed = true;
        currentOutputStream.close();
    }

}


<|start_filename|>src/test/resources/unicodePathExtra/GenerateZips.java<|end_filename|>
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;

import org.apache.commons.compress.archivers.zip.UnicodePathExtraField;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;

public class GenerateZips {
	
	private static void generate(String outfilename, boolean languageEncodingFlag) {
		try {
			ZipArchiveOutputStream zs = new ZipArchiveOutputStream(new File(outfilename));
			zs.setUseLanguageEncodingFlag(languageEncodingFlag);
			zs.setMethod(ZipArchiveOutputStream.STORED);
			ZipArchiveEntry ze = new ZipArchiveEntry("nameonly-name");
			ze.setTime(0);
			zs.putArchiveEntry(ze);
			//zs.write(nothing);
			zs.closeArchiveEntry();
			ze = new ZipArchiveEntry("goodextra-name");
			ze.setTime(0);
			ze.addExtraField(new UnicodePathExtraField("goodextra-extra", "goodextra-name".getBytes(Charset.forName("UTF-8"))));
			zs.putArchiveEntry(ze);
			//zs.write(nothing);
			zs.closeArchiveEntry();
			ze = new ZipArchiveEntry("badextra-name");
			ze.setTime(0);
			ze.addExtraField(new UnicodePathExtraField("badextra-extra", "bogus".getBytes(Charset.forName("UTF-8"))));
			zs.putArchiveEntry(ze);
			//zs.write(nothing);
			zs.closeArchiveEntry();
			zs.finish();
			zs.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		generate("efsclear.zip", false);
		// with the flag set, decoders tend to not look at the extra field
		generate("efsset.zip", true);
		System.out.println("done");
	}

}
