@Override
    public Path copy(final Path source, final Path target, final TransferStatus status, final ConnectionCallback callback, final StreamListener listener) throws BackgroundException {
        if(null == status.getStorageClass()) {
            // Keep same storage class
            status.setStorageClass(new S3StorageClassFeature(session, acl).getClass(source));
        }
        if(Encryption.Algorithm.NONE == status.getEncryption()) {
            // Keep encryption setting
            status.setEncryption(new S3EncryptionFeature(session, acl).getEncryption(source));
        }
        if(Acl.EMPTY == status.getAcl()) {
            // Apply non-standard ACL
            try {
                // Verify target bucket allows ACLs
                if(acl.getPermission(containerService.getContainer(target)).isEditable()) {
                    status.setAcl(acl.getPermission(source));
                }
            }
            catch(AccessDeniedException | InteroperabilityException e) {
                log.warn(String.format("Ignore failure %s", e));
            }
        }
        final S3Object destination = new S3WriteFeature(session, acl).getDetails(target, status);
        destination.setAcl(acl.toAcl(status.getAcl()));
        final Path bucket = containerService.getContainer(target);
        destination.setBucketName(bucket.isRoot() ? StringUtils.EMPTY : bucket.getName());
        destination.replaceAllMetadata(new HashMap<>(new S3MetadataFeature(session, acl).getMetadata(source)));
        final String versionId = this.copy(source, destination, status, listener);
        return target.withAttributes(new PathAttributes(source.attributes()).withVersionId(versionId));
    }
@Test
    public void testCopyFile() throws Exception {
        final Path container = new Path("test-eu-central-1-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
        final TransferStatus status = new TransferStatus();
        status.setMetadata(Collections.singletonMap("cyberduck", "m"));
        final Path test = new S3TouchFeature(session, new S3AccessControlListFeature(session)).touch(new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), status);
        final Path copy = new S3CopyFeature(session, new S3AccessControlListFeature(session)).copy(test,
                new Path(container, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener());
        assertTrue(new S3FindFeature(session, new S3AccessControlListFeature(session)).find(test));
        assertNull(copy.attributes().getVersionId());
        assertEquals("m", new S3MetadataFeature(session, new S3AccessControlListFeature(session)).getMetadata(copy).get("cyberduck"));
        new S3DefaultDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
        assertTrue(new S3FindFeature(session, new S3AccessControlListFeature(session)).find(copy));
        new S3DefaultDeleteFeature(session).delete(Collections.singletonList(copy), new DisabledLoginCallback(), new Delete.DisabledCallback());
    }