first commit
This commit is contained in:
commit
4d332ef662
27586 changed files with 3281783 additions and 0 deletions
201
rus/WEB-INF/lib/commons-compress-1.8.1_src/META-INF/LICENSE.txt
Normal file
201
rus/WEB-INF/lib/commons-compress-1.8.1_src/META-INF/LICENSE.txt
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
Manifest-Version: 1.0
|
||||
Export-Package: org.apache.commons.compress.archivers;version="1.8.1",
|
||||
org.apache.commons.compress.archivers.ar;version="1.8.1",org.apache.c
|
||||
ommons.compress.archivers.arj;version="1.8.1",org.apache.commons.comp
|
||||
ress.archivers.cpio;version="1.8.1",org.apache.commons.compress.archi
|
||||
vers.dump;version="1.8.1",org.apache.commons.compress.archivers.jar;v
|
||||
ersion="1.8.1",org.apache.commons.compress.archivers.sevenz;version="
|
||||
1.8.1",org.apache.commons.compress.archivers.tar;version="1.8.1",org.
|
||||
apache.commons.compress.archivers.zip;version="1.8.1",org.apache.comm
|
||||
ons.compress.changes;version="1.8.1",org.apache.commons.compress.comp
|
||||
ressors;version="1.8.1",org.apache.commons.compress.compressors.bzip2
|
||||
;version="1.8.1",org.apache.commons.compress.compressors.gzip;version
|
||||
="1.8.1",org.apache.commons.compress.compressors.lzma;version="1.8.1"
|
||||
,org.apache.commons.compress.compressors.pack200;version="1.8.1",org.
|
||||
apache.commons.compress.compressors.snappy;version="1.8.1",org.apache
|
||||
.commons.compress.compressors.xz;version="1.8.1",org.apache.commons.c
|
||||
ompress.compressors.z;version="1.8.1",org.apache.commons.compress.com
|
||||
pressors.z._internal_;version="1.8.1",org.apache.commons.compress.uti
|
||||
ls;version="1.8.1"
|
||||
Implementation-Title: Apache Commons Compress
|
||||
Built-By: bodewig
|
||||
Tool: Bnd-2.1.0.20130426-122213
|
||||
Implementation-Vendor: The Apache Software Foundation
|
||||
Implementation-Vendor-Id: org.apache
|
||||
Specification-Title: Apache Commons Compress
|
||||
Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||
Bundle-SymbolicName: org.apache.commons.compress
|
||||
Extension-Name: org.apache.commons.compress
|
||||
X-Compile-Target-JDK: 1.5
|
||||
Implementation-Version: 1.8.1
|
||||
Specification-Vendor: The Apache Software Foundation
|
||||
Bundle-Name: Apache Commons Compress
|
||||
Created-By: Apache Maven Bundle Plugin
|
||||
X-Compile-Source-JDK: 1.5
|
||||
Bundle-Vendor: The Apache Software Foundation
|
||||
Build-Jdk: 1.6.0_31
|
||||
Bundle-Version: 1.8.1
|
||||
Bnd-LastModified: 1399661128482
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Description: Apache Commons Compress software defines an API fo
|
||||
r working with compression and archive formats.These include: bzip2,
|
||||
gzip, pack200, lzma, xz, Snappy, traditional Unix Compress and ar, cp
|
||||
io, jar, tar, zip, dump, 7z, arj.
|
||||
Bundle-DocURL: http://commons.apache.org/proper/commons-compress/
|
||||
Import-Package: org.tukaani.xz;resolution:=optional
|
||||
Include-Resource: META-INF/LICENSE.txt=LICENSE.txt,META-INF/NOTICE.txt
|
||||
=NOTICE.txt
|
||||
Specification-Version: 1.8.1
|
||||
Implementation-Build: trunk@r1593589; 2014-05-09 20:44:50+0200
|
||||
Main-Class: org.apache.commons.compress.archivers.Lister
|
||||
Archiver-Version: Plexus Archiver
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
Apache Commons Compress
|
||||
Copyright 2002-2014 The Apache Software Foundation
|
||||
|
||||
This product includes software developed at
|
||||
The Apache Software Foundation (http://www.apache.org/).
|
||||
|
||||
The files in the package org.apache.commons.compress.archivers.sevenz
|
||||
were derived from the LZMA SDK, version 9.20 (C/ and CPP/7zip/),
|
||||
which has been placed in the public domain:
|
||||
|
||||
"LZMA SDK is placed in the public domain." (http://www.7-zip.org/sdk.html)
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#Generated by Maven
|
||||
#Fri May 09 20:47:40 CEST 2014
|
||||
version=1.8.1
|
||||
groupId=org.apache.commons
|
||||
artifactId=commons-compress
|
||||
|
|
@ -0,0 +1,342 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-parent</artifactId>
|
||||
<version>33</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
<version>1.8.1</version>
|
||||
<name>Apache Commons Compress</name>
|
||||
<url>http://commons.apache.org/proper/commons-compress/</url>
|
||||
<!-- The description is not indented to make it look better in the release notes -->
|
||||
<description>
|
||||
Apache Commons Compress software defines an API for working with compression and archive formats.
|
||||
These include: bzip2, gzip, pack200, lzma, xz, Snappy, traditional Unix Compress and ar, cpio, jar, tar, zip, dump, 7z, arj.
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>1.5</maven.compiler.source>
|
||||
<maven.compiler.target>1.5</maven.compiler.target>
|
||||
<commons.componentid>compress</commons.componentid>
|
||||
<commons.jira.id>COMPRESS</commons.jira.id>
|
||||
<commons.jira.pid>12310904</commons.jira.pid>
|
||||
<!-- configuration bits for cutting a release candidate -->
|
||||
<commons.release.version>${project.version}</commons.release.version>
|
||||
<commons.rc.version>RC1</commons.rc.version>
|
||||
</properties>
|
||||
|
||||
<issueManagement>
|
||||
<system>jira</system>
|
||||
<url>http://issues.apache.org/jira/browse/COMPRESS</url>
|
||||
</issueManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.tukaani</groupId>
|
||||
<artifactId>xz</artifactId>
|
||||
<version>1.5</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<name>Torsten Curdt</name>
|
||||
<id>tcurdt</id>
|
||||
<email>tcurdt at apache.org</email>
|
||||
</developer>
|
||||
<developer>
|
||||
<name>Stefan Bodewig</name>
|
||||
<id>bodewig</id>
|
||||
<email>bodewig at apache.org</email>
|
||||
</developer>
|
||||
<developer>
|
||||
<name>Sebastian Bazley</name>
|
||||
<id>sebb</id>
|
||||
<email>sebb at apache.org</email>
|
||||
</developer>
|
||||
<developer>
|
||||
<name>Christian Grobmeier</name>
|
||||
<id>grobmeier</id>
|
||||
<email>grobmeier at apache.org</email>
|
||||
</developer>
|
||||
<developer>
|
||||
<name>Julius Davies</name>
|
||||
<id>julius</id>
|
||||
<email>julius at apache.org</email>
|
||||
</developer>
|
||||
<developer>
|
||||
<name>Damjan Jovanovic</name>
|
||||
<id>damjan</id>
|
||||
<email>damjan at apache.org</email>
|
||||
</developer>
|
||||
<developer>
|
||||
<name>Emmanuel Bourg</name>
|
||||
<id>ebourg</id>
|
||||
<email>ebourg at apache.org</email>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<contributors>
|
||||
<contributor>
|
||||
<name>Wolfgang Glas</name>
|
||||
<email>wolfgang.glas at ev-i.at</email>
|
||||
</contributor>
|
||||
<contributor>
|
||||
<name>Christian Kohlschütte</name>
|
||||
<email>ck@newsclub.de</email>
|
||||
</contributor>
|
||||
<contributor>
|
||||
<name>Bear Giles</name>
|
||||
<email>bgiles@coyotesong.com</email>
|
||||
</contributor>
|
||||
<contributor>
|
||||
<name>Michael Kuss</name>
|
||||
<email>mail at michael minus kuss.de</email>
|
||||
</contributor>
|
||||
<contributor>
|
||||
<name>Lasse Collin</name>
|
||||
<email>lasse.collin@tukaani.org</email>
|
||||
</contributor>
|
||||
<contributor>
|
||||
<name>John Kodis</name>
|
||||
</contributor>
|
||||
<contributor>
|
||||
<name>BELUGA BEHR</name>
|
||||
</contributor>
|
||||
</contributors>
|
||||
|
||||
<scm>
|
||||
<connection>scm:svn:http://svn.apache.org/repos/asf/commons/proper/compress/tags/COMPRESS-1.8.1</connection>
|
||||
<developerConnection>scm:svn:https://svn.apache.org/repos/asf/commons/proper/compress/tags/COMPRESS-1.8.1</developerConnection>
|
||||
<url>http://svn.apache.org/repos/asf/commons/proper/compress/tags/COMPRESS-1.8.1</url>
|
||||
</scm>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<!-- create the source and binary assemblies -->
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>src/main/assembly/bin.xml</descriptor>
|
||||
<descriptor>src/main/assembly/src.xml</descriptor>
|
||||
</descriptors>
|
||||
<tarLongFileMode>gnu</tarLongFileMode>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestEntries>
|
||||
<Main-Class>org.apache.commons.compress.archivers.Lister</Main-Class>
|
||||
<Extension-Name>org.apache.commons.compress</Extension-Name>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Import-Package>org.tukaani.xz;resolution:=optional</Import-Package>
|
||||
</instructions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-scm-publish-plugin</artifactId>
|
||||
<configuration>
|
||||
<ignorePathsToDelete>
|
||||
<ignorePathToDelete>javadocs</ignorePathToDelete>
|
||||
</ignorePathsToDelete>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<!-- generate the changes report from changes.xml and link to JIRA -->
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-changes-plugin</artifactId>
|
||||
<version>${commons.changes.version}</version>
|
||||
<configuration>
|
||||
<issueLinkTemplatePerSystem>
|
||||
<default>%URL%/%ISSUE%</default>
|
||||
</issueLinkTemplatePerSystem>
|
||||
</configuration>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
<report>changes-report</report>
|
||||
<report>jira-report</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<!-- generate a code coverage report -->
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<!-- generate the PMD reports -->
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-pmd-plugin</artifactId>
|
||||
<version>2.5</version>
|
||||
<configuration>
|
||||
<minimumTokens>200</minimumTokens>
|
||||
<targetJdk>${maven.compiler.source}</targetJdk>
|
||||
<rulesets>
|
||||
<ruleset>${basedir}/pmd-ruleset.xml</ruleset>
|
||||
</rulesets>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- Override Javadoc config in parent pom to add JCIP tags -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<configuration>
|
||||
<quiet>true</quiet>
|
||||
<source>${maven.compiler.source}</source>
|
||||
<encoding>${commons.encoding}</encoding>
|
||||
<docEncoding>${commons.docEncoding}</docEncoding>
|
||||
<linksource>true</linksource>
|
||||
<links>
|
||||
<link>${commons.javadoc.java.link}</link>
|
||||
<link>${commons.javadoc.javaee.link}</link>
|
||||
</links>
|
||||
<tags>
|
||||
<tag>
|
||||
<name>Immutable</name>
|
||||
<placement>a</placement>
|
||||
<head>This class is immutable</head>
|
||||
</tag>
|
||||
<tag>
|
||||
<name>NotThreadSafe</name>
|
||||
<placement>a</placement>
|
||||
<head>This class is not thread-safe</head>
|
||||
</tag>
|
||||
<tag>
|
||||
<name>ThreadSafe</name>
|
||||
<placement>a</placement>
|
||||
<head>This class is thread-safe</head>
|
||||
</tag>
|
||||
</tags>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<version>2.5.3</version>
|
||||
<configuration>
|
||||
<threshold>Normal</threshold>
|
||||
<effort>Default</effort>
|
||||
<excludeFilterFile>${basedir}/findbugs-exclude-filter.xml</excludeFilterFile>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.rat</groupId>
|
||||
<artifactId>apache-rat-plugin</artifactId>
|
||||
<version>${commons.rat.version}</version>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<!-- files used during tests -->
|
||||
<exclude>src/test/resources/**</exclude>
|
||||
<!-- proposal text without license -->
|
||||
<exclude>PROPOSAL.txt</exclude>
|
||||
<exclude>.pmd</exclude>
|
||||
<exclude>.gitignore</exclude>
|
||||
<exclude>.gitattributes</exclude>
|
||||
<exclude>.projectile</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
|
||||
<profiles>
|
||||
<!-- Add long running tests as **/*IT.java -->
|
||||
<profile>
|
||||
<id>run-zipit</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>process-test-resources</phase>
|
||||
<configuration>
|
||||
<target>
|
||||
<untar src="${basedir}/src/test/resources/zip64support.tar.bz2"
|
||||
dest="${project.build.testOutputDirectory}"
|
||||
compression="bzip2"/>
|
||||
</target>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/zip/*IT.java</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>run-tarit</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/tar/*IT.java</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package org.apache.commons.compress.archivers;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public interface ArchiveEntry {
|
||||
public static final long SIZE_UNKNOWN = -1L;
|
||||
|
||||
String getName();
|
||||
|
||||
long getSize();
|
||||
|
||||
boolean isDirectory();
|
||||
|
||||
Date getLastModifiedDate();
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package org.apache.commons.compress.archivers;
|
||||
|
||||
public class ArchiveException extends Exception {
|
||||
private static final long serialVersionUID = 2772690708123267100L;
|
||||
|
||||
public ArchiveException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ArchiveException(String message, Exception cause) {
|
||||
super(message);
|
||||
initCause(cause);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package org.apache.commons.compress.archivers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public abstract class ArchiveInputStream extends InputStream {
|
||||
private final byte[] SINGLE = new byte[1];
|
||||
|
||||
private static final int BYTE_MASK = 255;
|
||||
|
||||
private long bytesRead = 0L;
|
||||
|
||||
public abstract ArchiveEntry getNextEntry() throws IOException;
|
||||
|
||||
public int read() throws IOException {
|
||||
int num = read(this.SINGLE, 0, 1);
|
||||
return (num == -1) ? -1 : (this.SINGLE[0] & 0xFF);
|
||||
}
|
||||
|
||||
protected void count(int read) {
|
||||
count((long)read);
|
||||
}
|
||||
|
||||
protected void count(long read) {
|
||||
if (read != -1L)
|
||||
this.bytesRead += read;
|
||||
}
|
||||
|
||||
protected void pushedBackBytes(long pushedBack) {
|
||||
this.bytesRead -= pushedBack;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getCount() {
|
||||
return (int)this.bytesRead;
|
||||
}
|
||||
|
||||
public long getBytesRead() {
|
||||
return this.bytesRead;
|
||||
}
|
||||
|
||||
public boolean canReadEntryData(ArchiveEntry archiveEntry) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package org.apache.commons.compress.archivers;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public abstract class ArchiveOutputStream extends OutputStream {
|
||||
private final byte[] oneByte = new byte[1];
|
||||
|
||||
static final int BYTE_MASK = 255;
|
||||
|
||||
private long bytesWritten = 0L;
|
||||
|
||||
public abstract void putArchiveEntry(ArchiveEntry paramArchiveEntry) throws IOException;
|
||||
|
||||
public abstract void closeArchiveEntry() throws IOException;
|
||||
|
||||
public abstract void finish() throws IOException;
|
||||
|
||||
public abstract ArchiveEntry createArchiveEntry(File paramFile, String paramString) throws IOException;
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
this.oneByte[0] = (byte)(b & 0xFF);
|
||||
write(this.oneByte, 0, 1);
|
||||
}
|
||||
|
||||
protected void count(int written) {
|
||||
count((long)written);
|
||||
}
|
||||
|
||||
protected void count(long written) {
|
||||
if (written != -1L)
|
||||
this.bytesWritten += written;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getCount() {
|
||||
return (int)this.bytesWritten;
|
||||
}
|
||||
|
||||
public long getBytesWritten() {
|
||||
return this.bytesWritten;
|
||||
}
|
||||
|
||||
public boolean canWriteEntryData(ArchiveEntry archiveEntry) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
package org.apache.commons.compress.archivers;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import org.apache.commons.compress.archivers.ar.ArArchiveInputStream;
|
||||
import org.apache.commons.compress.archivers.ar.ArArchiveOutputStream;
|
||||
import org.apache.commons.compress.archivers.arj.ArjArchiveInputStream;
|
||||
import org.apache.commons.compress.archivers.cpio.CpioArchiveInputStream;
|
||||
import org.apache.commons.compress.archivers.cpio.CpioArchiveOutputStream;
|
||||
import org.apache.commons.compress.archivers.dump.DumpArchiveInputStream;
|
||||
import org.apache.commons.compress.archivers.jar.JarArchiveInputStream;
|
||||
import org.apache.commons.compress.archivers.jar.JarArchiveOutputStream;
|
||||
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
|
||||
public class ArchiveStreamFactory {
|
||||
public static final String AR = "ar";
|
||||
|
||||
public static final String ARJ = "arj";
|
||||
|
||||
public static final String CPIO = "cpio";
|
||||
|
||||
public static final String DUMP = "dump";
|
||||
|
||||
public static final String JAR = "jar";
|
||||
|
||||
public static final String TAR = "tar";
|
||||
|
||||
public static final String ZIP = "zip";
|
||||
|
||||
public static final String SEVEN_Z = "7z";
|
||||
|
||||
private String entryEncoding = null;
|
||||
|
||||
public String getEntryEncoding() {
|
||||
return this.entryEncoding;
|
||||
}
|
||||
|
||||
public void setEntryEncoding(String entryEncoding) {
|
||||
this.entryEncoding = entryEncoding;
|
||||
}
|
||||
|
||||
public ArchiveInputStream createArchiveInputStream(String archiverName, InputStream in) throws ArchiveException {
|
||||
if (archiverName == null)
|
||||
throw new IllegalArgumentException("Archivername must not be null.");
|
||||
if (in == null)
|
||||
throw new IllegalArgumentException("InputStream must not be null.");
|
||||
if ("ar".equalsIgnoreCase(archiverName))
|
||||
return new ArArchiveInputStream(in);
|
||||
if ("arj".equalsIgnoreCase(archiverName)) {
|
||||
if (this.entryEncoding != null)
|
||||
return new ArjArchiveInputStream(in, this.entryEncoding);
|
||||
return new ArjArchiveInputStream(in);
|
||||
}
|
||||
if ("zip".equalsIgnoreCase(archiverName)) {
|
||||
if (this.entryEncoding != null)
|
||||
return new ZipArchiveInputStream(in, this.entryEncoding);
|
||||
return new ZipArchiveInputStream(in);
|
||||
}
|
||||
if ("tar".equalsIgnoreCase(archiverName)) {
|
||||
if (this.entryEncoding != null)
|
||||
return new TarArchiveInputStream(in, this.entryEncoding);
|
||||
return new TarArchiveInputStream(in);
|
||||
}
|
||||
if ("jar".equalsIgnoreCase(archiverName))
|
||||
return new JarArchiveInputStream(in);
|
||||
if ("cpio".equalsIgnoreCase(archiverName)) {
|
||||
if (this.entryEncoding != null)
|
||||
return new CpioArchiveInputStream(in, this.entryEncoding);
|
||||
return new CpioArchiveInputStream(in);
|
||||
}
|
||||
if ("dump".equalsIgnoreCase(archiverName)) {
|
||||
if (this.entryEncoding != null)
|
||||
return new DumpArchiveInputStream(in, this.entryEncoding);
|
||||
return new DumpArchiveInputStream(in);
|
||||
}
|
||||
if ("7z".equalsIgnoreCase(archiverName))
|
||||
throw new StreamingNotSupportedException("7z");
|
||||
throw new ArchiveException("Archiver: " + archiverName + " not found.");
|
||||
}
|
||||
|
||||
public ArchiveOutputStream createArchiveOutputStream(String archiverName, OutputStream out) throws ArchiveException {
|
||||
if (archiverName == null)
|
||||
throw new IllegalArgumentException("Archivername must not be null.");
|
||||
if (out == null)
|
||||
throw new IllegalArgumentException("OutputStream must not be null.");
|
||||
if ("ar".equalsIgnoreCase(archiverName))
|
||||
return new ArArchiveOutputStream(out);
|
||||
if ("zip".equalsIgnoreCase(archiverName)) {
|
||||
ZipArchiveOutputStream zip = new ZipArchiveOutputStream(out);
|
||||
if (this.entryEncoding != null)
|
||||
zip.setEncoding(this.entryEncoding);
|
||||
return zip;
|
||||
}
|
||||
if ("tar".equalsIgnoreCase(archiverName)) {
|
||||
if (this.entryEncoding != null)
|
||||
return new TarArchiveOutputStream(out, this.entryEncoding);
|
||||
return new TarArchiveOutputStream(out);
|
||||
}
|
||||
if ("jar".equalsIgnoreCase(archiverName))
|
||||
return new JarArchiveOutputStream(out);
|
||||
if ("cpio".equalsIgnoreCase(archiverName)) {
|
||||
if (this.entryEncoding != null)
|
||||
return new CpioArchiveOutputStream(out, this.entryEncoding);
|
||||
return new CpioArchiveOutputStream(out);
|
||||
}
|
||||
if ("7z".equalsIgnoreCase(archiverName))
|
||||
throw new StreamingNotSupportedException("7z");
|
||||
throw new ArchiveException("Archiver: " + archiverName + " not found.");
|
||||
}
|
||||
|
||||
public ArchiveInputStream createArchiveInputStream(InputStream in) throws ArchiveException {
|
||||
if (in == null)
|
||||
throw new IllegalArgumentException("Stream must not be null.");
|
||||
if (!in.markSupported())
|
||||
throw new IllegalArgumentException("Mark is not supported.");
|
||||
byte[] signature = new byte[12];
|
||||
in.mark(signature.length);
|
||||
try {
|
||||
int signatureLength = IOUtils.readFully(in, signature);
|
||||
in.reset();
|
||||
if (ZipArchiveInputStream.matches(signature, signatureLength)) {
|
||||
if (this.entryEncoding != null)
|
||||
return new ZipArchiveInputStream(in, this.entryEncoding);
|
||||
return new ZipArchiveInputStream(in);
|
||||
}
|
||||
if (JarArchiveInputStream.matches(signature, signatureLength))
|
||||
return new JarArchiveInputStream(in);
|
||||
if (ArArchiveInputStream.matches(signature, signatureLength))
|
||||
return new ArArchiveInputStream(in);
|
||||
if (CpioArchiveInputStream.matches(signature, signatureLength))
|
||||
return new CpioArchiveInputStream(in);
|
||||
if (ArjArchiveInputStream.matches(signature, signatureLength))
|
||||
return new ArjArchiveInputStream(in);
|
||||
if (SevenZFile.matches(signature, signatureLength))
|
||||
throw new StreamingNotSupportedException("7z");
|
||||
byte[] dumpsig = new byte[32];
|
||||
in.mark(dumpsig.length);
|
||||
signatureLength = IOUtils.readFully(in, dumpsig);
|
||||
in.reset();
|
||||
if (DumpArchiveInputStream.matches(dumpsig, signatureLength))
|
||||
return new DumpArchiveInputStream(in);
|
||||
byte[] tarheader = new byte[512];
|
||||
in.mark(tarheader.length);
|
||||
signatureLength = IOUtils.readFully(in, tarheader);
|
||||
in.reset();
|
||||
if (TarArchiveInputStream.matches(tarheader, signatureLength)) {
|
||||
if (this.entryEncoding != null)
|
||||
return new TarArchiveInputStream(in, this.entryEncoding);
|
||||
return new TarArchiveInputStream(in);
|
||||
}
|
||||
if (signatureLength >= 512) {
|
||||
TarArchiveInputStream tais = null;
|
||||
try {
|
||||
tais = new TarArchiveInputStream(new ByteArrayInputStream(tarheader));
|
||||
if (tais.getNextTarEntry().isCheckSumOK())
|
||||
return new TarArchiveInputStream(in);
|
||||
} catch (Exception e) {
|
||||
|
||||
} finally {
|
||||
IOUtils.closeQuietly(tais);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ArchiveException("Could not use reset and mark operations.", e);
|
||||
}
|
||||
throw new ArchiveException("No Archiver found for the stream signature");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package org.apache.commons.compress.archivers;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
public final class Lister {
|
||||
private static final ArchiveStreamFactory factory = new ArchiveStreamFactory();
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ArchiveInputStream ais;
|
||||
if (args.length == 0) {
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
System.out.println("Analysing " + args[0]);
|
||||
File f = new File(args[0]);
|
||||
if (!f.isFile())
|
||||
System.err.println(f + " doesn't exist or is a directory");
|
||||
InputStream fis = new BufferedInputStream(new FileInputStream(f));
|
||||
if (args.length > 1) {
|
||||
ais = factory.createArchiveInputStream(args[1], fis);
|
||||
} else {
|
||||
ais = factory.createArchiveInputStream(fis);
|
||||
}
|
||||
System.out.println("Created " + ais.toString());
|
||||
ArchiveEntry ae;
|
||||
while ((ae = ais.getNextEntry()) != null)
|
||||
System.out.println(ae.getName());
|
||||
ais.close();
|
||||
fis.close();
|
||||
}
|
||||
|
||||
private static void usage() {
|
||||
System.out.println("Parameters: archive-name [archive-type]");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package org.apache.commons.compress.archivers;
|
||||
|
||||
public class StreamingNotSupportedException extends ArchiveException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String format;
|
||||
|
||||
public StreamingNotSupportedException(String format) {
|
||||
super("The " + format + " doesn't support streaming.");
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public String getFormat() {
|
||||
return this.format;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
package org.apache.commons.compress.archivers.ar;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
|
||||
public class ArArchiveEntry implements ArchiveEntry {
|
||||
public static final String HEADER = "!<arch>\n";
|
||||
|
||||
public static final String TRAILER = "`\n";
|
||||
|
||||
private final String name;
|
||||
|
||||
private final int userId;
|
||||
|
||||
private final int groupId;
|
||||
|
||||
private final int mode;
|
||||
|
||||
private static final int DEFAULT_MODE = 33188;
|
||||
|
||||
private final long lastModified;
|
||||
|
||||
private final long length;
|
||||
|
||||
public ArArchiveEntry(String name, long length) {
|
||||
this(name, length, 0, 0, 33188, System.currentTimeMillis() / 1000L);
|
||||
}
|
||||
|
||||
public ArArchiveEntry(String name, long length, int userId, int groupId, int mode, long lastModified) {
|
||||
this.name = name;
|
||||
this.length = length;
|
||||
this.userId = userId;
|
||||
this.groupId = groupId;
|
||||
this.mode = mode;
|
||||
this.lastModified = lastModified;
|
||||
}
|
||||
|
||||
public ArArchiveEntry(File inputFile, String entryName) {
|
||||
this(entryName, inputFile.isFile() ? inputFile.length() : 0L, 0, 0, 33188, inputFile.lastModified() / 1000L);
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return getLength();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public int getUserId() {
|
||||
return this.userId;
|
||||
}
|
||||
|
||||
public int getGroupId() {
|
||||
return this.groupId;
|
||||
}
|
||||
|
||||
public int getMode() {
|
||||
return this.mode;
|
||||
}
|
||||
|
||||
public long getLastModified() {
|
||||
return this.lastModified;
|
||||
}
|
||||
|
||||
public Date getLastModifiedDate() {
|
||||
return new Date(1000L * getLastModified());
|
||||
}
|
||||
|
||||
public long getLength() {
|
||||
return this.length;
|
||||
}
|
||||
|
||||
public boolean isDirectory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int prime = 31;
|
||||
int result = 1;
|
||||
result = 31 * result + ((this.name == null) ? 0 : this.name.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
ArArchiveEntry other = (ArArchiveEntry)obj;
|
||||
if (this.name == null) {
|
||||
if (other.name != null)
|
||||
return false;
|
||||
} else if (!this.name.equals(other.name)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
package org.apache.commons.compress.archivers.ar;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.ArchiveInputStream;
|
||||
import org.apache.commons.compress.utils.ArchiveUtils;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
|
||||
public class ArArchiveInputStream extends ArchiveInputStream {
|
||||
private final InputStream input;
|
||||
|
||||
private long offset = 0L;
|
||||
|
||||
private boolean closed;
|
||||
|
||||
private ArArchiveEntry currentEntry = null;
|
||||
|
||||
private byte[] namebuffer = null;
|
||||
|
||||
private long entryOffset = -1L;
|
||||
|
||||
private final byte[] NAME_BUF = new byte[16];
|
||||
|
||||
private final byte[] LAST_MODIFIED_BUF = new byte[12];
|
||||
|
||||
private final byte[] ID_BUF = new byte[6];
|
||||
|
||||
private final byte[] FILE_MODE_BUF = new byte[8];
|
||||
|
||||
private final byte[] LENGTH_BUF = new byte[10];
|
||||
|
||||
static final String BSD_LONGNAME_PREFIX = "#1/";
|
||||
|
||||
public ArArchiveInputStream(InputStream pInput) {
|
||||
this.input = pInput;
|
||||
this.closed = false;
|
||||
}
|
||||
|
||||
public ArArchiveEntry getNextArEntry() throws IOException {
|
||||
if (this.currentEntry != null) {
|
||||
long entryEnd = this.entryOffset + this.currentEntry.getLength();
|
||||
IOUtils.skip(this, entryEnd - this.offset);
|
||||
this.currentEntry = null;
|
||||
}
|
||||
if (this.offset == 0L) {
|
||||
byte[] arrayOfByte1 = ArchiveUtils.toAsciiBytes("!<arch>\n");
|
||||
byte[] arrayOfByte2 = new byte[arrayOfByte1.length];
|
||||
int j = IOUtils.readFully(this, arrayOfByte2);
|
||||
if (j != arrayOfByte1.length)
|
||||
throw new IOException("failed to read header. Occured at byte: " + getBytesRead());
|
||||
for (int k = 0; k < arrayOfByte1.length; k++) {
|
||||
if (arrayOfByte1[k] != arrayOfByte2[k])
|
||||
throw new IOException("invalid header " + ArchiveUtils.toAsciiString(arrayOfByte2));
|
||||
}
|
||||
}
|
||||
if (this.offset % 2L != 0L && read() < 0)
|
||||
return null;
|
||||
if (this.input.available() == 0)
|
||||
return null;
|
||||
IOUtils.readFully(this, this.NAME_BUF);
|
||||
IOUtils.readFully(this, this.LAST_MODIFIED_BUF);
|
||||
IOUtils.readFully(this, this.ID_BUF);
|
||||
int userId = asInt(this.ID_BUF, true);
|
||||
IOUtils.readFully(this, this.ID_BUF);
|
||||
IOUtils.readFully(this, this.FILE_MODE_BUF);
|
||||
IOUtils.readFully(this, this.LENGTH_BUF);
|
||||
byte[] expected = ArchiveUtils.toAsciiBytes("`\n");
|
||||
byte[] realized = new byte[expected.length];
|
||||
int read = IOUtils.readFully(this, realized);
|
||||
if (read != expected.length)
|
||||
throw new IOException("failed to read entry trailer. Occured at byte: " + getBytesRead());
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
if (expected[i] != realized[i])
|
||||
throw new IOException("invalid entry trailer. not read the content? Occured at byte: " + getBytesRead());
|
||||
}
|
||||
this.entryOffset = this.offset;
|
||||
String temp = ArchiveUtils.toAsciiString(this.NAME_BUF).trim();
|
||||
if (isGNUStringTable(temp)) {
|
||||
this.currentEntry = readGNUStringTable(this.LENGTH_BUF);
|
||||
return getNextArEntry();
|
||||
}
|
||||
long len = asLong(this.LENGTH_BUF);
|
||||
if (temp.endsWith("/")) {
|
||||
temp = temp.substring(0, temp.length() - 1);
|
||||
} else if (isGNULongName(temp)) {
|
||||
int off = Integer.parseInt(temp.substring(1));
|
||||
temp = getExtendedName(off);
|
||||
} else if (isBSDLongName(temp)) {
|
||||
temp = getBSDLongName(temp);
|
||||
int nameLen = temp.length();
|
||||
len -= (long)nameLen;
|
||||
this.entryOffset += (long)nameLen;
|
||||
}
|
||||
this.currentEntry = new ArArchiveEntry(temp, len, userId, asInt(this.ID_BUF, true), asInt(this.FILE_MODE_BUF, 8), asLong(this.LAST_MODIFIED_BUF));
|
||||
return this.currentEntry;
|
||||
}
|
||||
|
||||
private String getExtendedName(int offset) throws IOException {
|
||||
if (this.namebuffer == null)
|
||||
throw new IOException("Cannot process GNU long filename as no // record was found");
|
||||
for (int i = offset; i < this.namebuffer.length; i++) {
|
||||
if (this.namebuffer[i] == 10) {
|
||||
if (this.namebuffer[i - 1] == 47)
|
||||
i--;
|
||||
return ArchiveUtils.toAsciiString(this.namebuffer, offset, i - offset);
|
||||
}
|
||||
}
|
||||
throw new IOException("Failed to read entry: " + offset);
|
||||
}
|
||||
|
||||
private long asLong(byte[] input) {
|
||||
return Long.parseLong(ArchiveUtils.toAsciiString(input).trim());
|
||||
}
|
||||
|
||||
private int asInt(byte[] input) {
|
||||
return asInt(input, 10, false);
|
||||
}
|
||||
|
||||
private int asInt(byte[] input, boolean treatBlankAsZero) {
|
||||
return asInt(input, 10, treatBlankAsZero);
|
||||
}
|
||||
|
||||
private int asInt(byte[] input, int base) {
|
||||
return asInt(input, base, false);
|
||||
}
|
||||
|
||||
private int asInt(byte[] input, int base, boolean treatBlankAsZero) {
|
||||
String string = ArchiveUtils.toAsciiString(input).trim();
|
||||
if (string.length() == 0 && treatBlankAsZero)
|
||||
return 0;
|
||||
return Integer.parseInt(string, base);
|
||||
}
|
||||
|
||||
public ArchiveEntry getNextEntry() throws IOException {
|
||||
return getNextArEntry();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (!this.closed) {
|
||||
this.closed = true;
|
||||
this.input.close();
|
||||
}
|
||||
this.currentEntry = null;
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
int toRead = len;
|
||||
if (this.currentEntry != null) {
|
||||
long entryEnd = this.entryOffset + this.currentEntry.getLength();
|
||||
if (len > 0 && entryEnd > this.offset) {
|
||||
toRead = (int)Math.min((long)len, entryEnd - this.offset);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
int ret = this.input.read(b, off, toRead);
|
||||
count(ret);
|
||||
this.offset += (ret > 0) ? (long)ret : 0L;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static boolean matches(byte[] signature, int length) {
|
||||
if (length < 8)
|
||||
return false;
|
||||
if (signature[0] != 33)
|
||||
return false;
|
||||
if (signature[1] != 60)
|
||||
return false;
|
||||
if (signature[2] != 97)
|
||||
return false;
|
||||
if (signature[3] != 114)
|
||||
return false;
|
||||
if (signature[4] != 99)
|
||||
return false;
|
||||
if (signature[5] != 104)
|
||||
return false;
|
||||
if (signature[6] != 62)
|
||||
return false;
|
||||
if (signature[7] != 10)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static final int BSD_LONGNAME_PREFIX_LEN = "#1/".length();
|
||||
|
||||
private static final String BSD_LONGNAME_PATTERN = "^#1/\\d+";
|
||||
|
||||
private static final String GNU_STRING_TABLE_NAME = "//";
|
||||
|
||||
private static final String GNU_LONGNAME_PATTERN = "^/\\d+";
|
||||
|
||||
private static boolean isBSDLongName(String name) {
|
||||
return (name != null && name.matches("^#1/\\d+"));
|
||||
}
|
||||
|
||||
private String getBSDLongName(String bsdLongName) throws IOException {
|
||||
int nameLen = Integer.parseInt(bsdLongName.substring(BSD_LONGNAME_PREFIX_LEN));
|
||||
byte[] name = new byte[nameLen];
|
||||
int read = IOUtils.readFully(this.input, name);
|
||||
count(read);
|
||||
if (read != nameLen)
|
||||
throw new EOFException();
|
||||
return ArchiveUtils.toAsciiString(name);
|
||||
}
|
||||
|
||||
private static boolean isGNUStringTable(String name) {
|
||||
return "//".equals(name);
|
||||
}
|
||||
|
||||
private ArArchiveEntry readGNUStringTable(byte[] length) throws IOException {
|
||||
int bufflen = asInt(length);
|
||||
this.namebuffer = new byte[bufflen];
|
||||
int read = IOUtils.readFully(this, this.namebuffer, 0, bufflen);
|
||||
if (read != bufflen)
|
||||
throw new IOException("Failed to read complete // record: expected=" + bufflen + " read=" + read);
|
||||
return new ArArchiveEntry("//", (long)bufflen);
|
||||
}
|
||||
|
||||
private boolean isGNULongName(String name) {
|
||||
return (name != null && name.matches("^/\\d+"));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
package org.apache.commons.compress.archivers.ar;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.ArchiveOutputStream;
|
||||
import org.apache.commons.compress.utils.ArchiveUtils;
|
||||
|
||||
public class ArArchiveOutputStream extends ArchiveOutputStream {
|
||||
public static final int LONGFILE_ERROR = 0;
|
||||
|
||||
public static final int LONGFILE_BSD = 1;
|
||||
|
||||
private final OutputStream out;
|
||||
|
||||
private long entryOffset = 0L;
|
||||
|
||||
private ArArchiveEntry prevEntry;
|
||||
|
||||
private boolean haveUnclosedEntry = false;
|
||||
|
||||
private int longFileMode = 0;
|
||||
|
||||
private boolean finished = false;
|
||||
|
||||
public ArArchiveOutputStream(OutputStream pOut) {
|
||||
this.out = pOut;
|
||||
}
|
||||
|
||||
public void setLongFileMode(int longFileMode) {
|
||||
this.longFileMode = longFileMode;
|
||||
}
|
||||
|
||||
private long writeArchiveHeader() throws IOException {
|
||||
byte[] header = ArchiveUtils.toAsciiBytes("!<arch>\n");
|
||||
this.out.write(header);
|
||||
return (long)header.length;
|
||||
}
|
||||
|
||||
public void closeArchiveEntry() throws IOException {
|
||||
if (this.finished)
|
||||
throw new IOException("Stream has already been finished");
|
||||
if (this.prevEntry == null || !this.haveUnclosedEntry)
|
||||
throw new IOException("No current entry to close");
|
||||
if (this.entryOffset % 2L != 0L)
|
||||
this.out.write(10);
|
||||
this.haveUnclosedEntry = false;
|
||||
}
|
||||
|
||||
public void putArchiveEntry(ArchiveEntry pEntry) throws IOException {
|
||||
if (this.finished)
|
||||
throw new IOException("Stream has already been finished");
|
||||
ArArchiveEntry pArEntry = (ArArchiveEntry)pEntry;
|
||||
if (this.prevEntry == null) {
|
||||
writeArchiveHeader();
|
||||
} else {
|
||||
if (this.prevEntry.getLength() != this.entryOffset)
|
||||
throw new IOException("length does not match entry (" + this.prevEntry.getLength() + " != " + this.entryOffset);
|
||||
if (this.haveUnclosedEntry)
|
||||
closeArchiveEntry();
|
||||
}
|
||||
this.prevEntry = pArEntry;
|
||||
writeEntryHeader(pArEntry);
|
||||
this.entryOffset = 0L;
|
||||
this.haveUnclosedEntry = true;
|
||||
}
|
||||
|
||||
private long fill(long pOffset, long pNewOffset, char pFill) throws IOException {
|
||||
long diff = pNewOffset - pOffset;
|
||||
if (diff > 0L)
|
||||
for (int i = 0; (long)i < diff; i++)
|
||||
write(pFill);
|
||||
return pNewOffset;
|
||||
}
|
||||
|
||||
private long write(String data) throws IOException {
|
||||
byte[] bytes = data.getBytes("ascii");
|
||||
write(bytes);
|
||||
return (long)bytes.length;
|
||||
}
|
||||
|
||||
private long writeEntryHeader(ArArchiveEntry pEntry) throws IOException {
|
||||
long offset = 0L;
|
||||
boolean mustAppendName = false;
|
||||
String n = pEntry.getName();
|
||||
if (0 == this.longFileMode && n.length() > 16)
|
||||
throw new IOException("filename too long, > 16 chars: " + n);
|
||||
if (1 == this.longFileMode && (n.length() > 16 || n.indexOf(" ") > -1)) {
|
||||
mustAppendName = true;
|
||||
offset += write("#1/" + String.valueOf(n.length()));
|
||||
} else {
|
||||
offset += write(n);
|
||||
}
|
||||
offset = fill(offset, 16L, ' ');
|
||||
String m = "" + pEntry.getLastModified();
|
||||
if (m.length() > 12)
|
||||
throw new IOException("modified too long");
|
||||
offset += write(m);
|
||||
offset = fill(offset, 28L, ' ');
|
||||
String u = "" + pEntry.getUserId();
|
||||
if (u.length() > 6)
|
||||
throw new IOException("userid too long");
|
||||
offset += write(u);
|
||||
offset = fill(offset, 34L, ' ');
|
||||
String g = "" + pEntry.getGroupId();
|
||||
if (g.length() > 6)
|
||||
throw new IOException("groupid too long");
|
||||
offset += write(g);
|
||||
offset = fill(offset, 40L, ' ');
|
||||
String fm = "" + Integer.toString(pEntry.getMode(), 8);
|
||||
if (fm.length() > 8)
|
||||
throw new IOException("filemode too long");
|
||||
offset += write(fm);
|
||||
offset = fill(offset, 48L, ' ');
|
||||
String s = String.valueOf(pEntry.getLength() + (long)(mustAppendName ? n.length() : 0L));
|
||||
if (s.length() > 10)
|
||||
throw new IOException("size too long");
|
||||
offset += write(s);
|
||||
offset = fill(offset, 58L, ' ');
|
||||
offset += write("`\n");
|
||||
if (mustAppendName)
|
||||
offset += write(n);
|
||||
return offset;
|
||||
}
|
||||
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
this.out.write(b, off, len);
|
||||
count(len);
|
||||
this.entryOffset += (long)len;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (!this.finished)
|
||||
finish();
|
||||
this.out.close();
|
||||
this.prevEntry = null;
|
||||
}
|
||||
|
||||
public ArchiveEntry createArchiveEntry(File inputFile, String entryName) throws IOException {
|
||||
if (this.finished)
|
||||
throw new IOException("Stream has already been finished");
|
||||
return new ArArchiveEntry(inputFile, entryName);
|
||||
}
|
||||
|
||||
public void finish() throws IOException {
|
||||
if (this.haveUnclosedEntry)
|
||||
throw new IOException("This archive contains unclosed entries.");
|
||||
if (this.finished)
|
||||
throw new IOException("This archive has already been finished");
|
||||
this.finished = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package org.apache.commons.compress.archivers.arj;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
import java.util.regex.Matcher;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipUtil;
|
||||
|
||||
public class ArjArchiveEntry implements ArchiveEntry {
|
||||
private final LocalFileHeader localFileHeader;
|
||||
|
||||
public ArjArchiveEntry() {
|
||||
this.localFileHeader = new LocalFileHeader();
|
||||
}
|
||||
|
||||
ArjArchiveEntry(LocalFileHeader localFileHeader) {
|
||||
this.localFileHeader = localFileHeader;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
if ((this.localFileHeader.arjFlags & 0x10) != 0)
|
||||
return this.localFileHeader.name.replaceAll("/", Matcher.quoteReplacement(File.separator));
|
||||
return this.localFileHeader.name;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return this.localFileHeader.originalSize;
|
||||
}
|
||||
|
||||
public boolean isDirectory() {
|
||||
return (this.localFileHeader.fileType == 3);
|
||||
}
|
||||
|
||||
public Date getLastModifiedDate() {
|
||||
long ts = isHostOsUnix() ? ((long)this.localFileHeader.dateTimeModified * 1000L) : ZipUtil.dosToJavaTime(0xFFFFFFFFL & (long)this.localFileHeader.dateTimeModified);
|
||||
return new Date(ts);
|
||||
}
|
||||
|
||||
public int getMode() {
|
||||
return this.localFileHeader.fileAccessMode;
|
||||
}
|
||||
|
||||
public int getUnixMode() {
|
||||
return isHostOsUnix() ? getMode() : 0;
|
||||
}
|
||||
|
||||
public int getHostOs() {
|
||||
return this.localFileHeader.hostOS;
|
||||
}
|
||||
|
||||
public boolean isHostOsUnix() {
|
||||
return (getHostOs() == 2 || getHostOs() == 8);
|
||||
}
|
||||
|
||||
int getMethod() {
|
||||
return this.localFileHeader.method;
|
||||
}
|
||||
|
||||
public static class HostOs {
|
||||
public static final int DOS = 0;
|
||||
|
||||
public static final int PRIMOS = 1;
|
||||
|
||||
public static final int UNIX = 2;
|
||||
|
||||
public static final int AMIGA = 3;
|
||||
|
||||
public static final int MAC_OS = 4;
|
||||
|
||||
public static final int OS_2 = 5;
|
||||
|
||||
public static final int APPLE_GS = 6;
|
||||
|
||||
public static final int ATARI_ST = 7;
|
||||
|
||||
public static final int NEXT = 8;
|
||||
|
||||
public static final int VAX_VMS = 9;
|
||||
|
||||
public static final int WIN95 = 10;
|
||||
|
||||
public static final int WIN32 = 11;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,260 @@
|
|||
package org.apache.commons.compress.archivers.arj;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.zip.CRC32;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.ArchiveException;
|
||||
import org.apache.commons.compress.archivers.ArchiveInputStream;
|
||||
import org.apache.commons.compress.utils.BoundedInputStream;
|
||||
import org.apache.commons.compress.utils.CRC32VerifyingInputStream;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
|
||||
public class ArjArchiveInputStream extends ArchiveInputStream {
|
||||
private static final int ARJ_MAGIC_1 = 96;
|
||||
|
||||
private static final int ARJ_MAGIC_2 = 234;
|
||||
|
||||
private final DataInputStream in;
|
||||
|
||||
private final String charsetName;
|
||||
|
||||
private final MainHeader mainHeader;
|
||||
|
||||
private LocalFileHeader currentLocalFileHeader = null;
|
||||
|
||||
private InputStream currentInputStream = null;
|
||||
|
||||
public ArjArchiveInputStream(InputStream inputStream, String charsetName) throws ArchiveException {
|
||||
this.in = new DataInputStream(inputStream);
|
||||
this.charsetName = charsetName;
|
||||
try {
|
||||
this.mainHeader = readMainHeader();
|
||||
if ((this.mainHeader.arjFlags & 0x1) != 0)
|
||||
throw new ArchiveException("Encrypted ARJ files are unsupported");
|
||||
if ((this.mainHeader.arjFlags & 0x4) != 0)
|
||||
throw new ArchiveException("Multi-volume ARJ files are unsupported");
|
||||
} catch (IOException ioException) {
|
||||
throw new ArchiveException(ioException.getMessage(), ioException);
|
||||
}
|
||||
}
|
||||
|
||||
public ArjArchiveInputStream(InputStream inputStream) throws ArchiveException {
|
||||
this(inputStream, "CP437");
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
this.in.close();
|
||||
}
|
||||
|
||||
private int read8(DataInputStream dataIn) throws IOException {
|
||||
int value = dataIn.readUnsignedByte();
|
||||
count(1);
|
||||
return value;
|
||||
}
|
||||
|
||||
private int read16(DataInputStream dataIn) throws IOException {
|
||||
int value = dataIn.readUnsignedShort();
|
||||
count(2);
|
||||
return Integer.reverseBytes(value) >>> 16;
|
||||
}
|
||||
|
||||
private int read32(DataInputStream dataIn) throws IOException {
|
||||
int value = dataIn.readInt();
|
||||
count(4);
|
||||
return Integer.reverseBytes(value);
|
||||
}
|
||||
|
||||
private String readString(DataInputStream dataIn) throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
int nextByte;
|
||||
while ((nextByte = dataIn.readUnsignedByte()) != 0)
|
||||
buffer.write(nextByte);
|
||||
if (this.charsetName != null)
|
||||
return new String(buffer.toByteArray(), this.charsetName);
|
||||
return new String(buffer.toByteArray());
|
||||
}
|
||||
|
||||
private void readFully(DataInputStream dataIn, byte[] b) throws IOException {
|
||||
dataIn.readFully(b);
|
||||
count(b.length);
|
||||
}
|
||||
|
||||
private byte[] readHeader() throws IOException {
|
||||
boolean found = false;
|
||||
byte[] basicHeaderBytes = null;
|
||||
while (true) {
|
||||
int first = 0;
|
||||
int second = read8(this.in);
|
||||
do {
|
||||
first = second;
|
||||
second = read8(this.in);
|
||||
} while (first != 96 && second != 234);
|
||||
int basicHeaderSize = read16(this.in);
|
||||
if (basicHeaderSize == 0)
|
||||
return null;
|
||||
if (basicHeaderSize <= 2600) {
|
||||
basicHeaderBytes = new byte[basicHeaderSize];
|
||||
readFully(this.in, basicHeaderBytes);
|
||||
long basicHeaderCrc32 = (long)read32(this.in) & 0xFFFFFFFFL;
|
||||
CRC32 crc32 = new CRC32();
|
||||
crc32.update(basicHeaderBytes);
|
||||
if (basicHeaderCrc32 == crc32.getValue())
|
||||
found = true;
|
||||
}
|
||||
if (found)
|
||||
return basicHeaderBytes;
|
||||
}
|
||||
}
|
||||
|
||||
private MainHeader readMainHeader() throws IOException {
|
||||
byte[] basicHeaderBytes = readHeader();
|
||||
if (basicHeaderBytes == null)
|
||||
throw new IOException("Archive ends without any headers");
|
||||
DataInputStream basicHeader = new DataInputStream(new ByteArrayInputStream(basicHeaderBytes));
|
||||
int firstHeaderSize = basicHeader.readUnsignedByte();
|
||||
byte[] firstHeaderBytes = new byte[firstHeaderSize - 1];
|
||||
basicHeader.readFully(firstHeaderBytes);
|
||||
DataInputStream firstHeader = new DataInputStream(new ByteArrayInputStream(firstHeaderBytes));
|
||||
MainHeader hdr = new MainHeader();
|
||||
hdr.archiverVersionNumber = firstHeader.readUnsignedByte();
|
||||
hdr.minVersionToExtract = firstHeader.readUnsignedByte();
|
||||
hdr.hostOS = firstHeader.readUnsignedByte();
|
||||
hdr.arjFlags = firstHeader.readUnsignedByte();
|
||||
hdr.securityVersion = firstHeader.readUnsignedByte();
|
||||
hdr.fileType = firstHeader.readUnsignedByte();
|
||||
hdr.reserved = firstHeader.readUnsignedByte();
|
||||
hdr.dateTimeCreated = read32(firstHeader);
|
||||
hdr.dateTimeModified = read32(firstHeader);
|
||||
hdr.archiveSize = 0xFFFFFFFFL & (long)read32(firstHeader);
|
||||
hdr.securityEnvelopeFilePosition = read32(firstHeader);
|
||||
hdr.fileSpecPosition = read16(firstHeader);
|
||||
hdr.securityEnvelopeLength = read16(firstHeader);
|
||||
pushedBackBytes(20L);
|
||||
hdr.encryptionVersion = firstHeader.readUnsignedByte();
|
||||
hdr.lastChapter = firstHeader.readUnsignedByte();
|
||||
if (firstHeaderSize >= 33) {
|
||||
hdr.arjProtectionFactor = firstHeader.readUnsignedByte();
|
||||
hdr.arjFlags2 = firstHeader.readUnsignedByte();
|
||||
firstHeader.readUnsignedByte();
|
||||
firstHeader.readUnsignedByte();
|
||||
}
|
||||
hdr.name = readString(basicHeader);
|
||||
hdr.comment = readString(basicHeader);
|
||||
int extendedHeaderSize = read16(this.in);
|
||||
if (extendedHeaderSize > 0) {
|
||||
hdr.extendedHeaderBytes = new byte[extendedHeaderSize];
|
||||
readFully(this.in, hdr.extendedHeaderBytes);
|
||||
long extendedHeaderCrc32 = 0xFFFFFFFFL & (long)read32(this.in);
|
||||
CRC32 crc32 = new CRC32();
|
||||
crc32.update(hdr.extendedHeaderBytes);
|
||||
if (extendedHeaderCrc32 != crc32.getValue())
|
||||
throw new IOException("Extended header CRC32 verification failure");
|
||||
}
|
||||
return hdr;
|
||||
}
|
||||
|
||||
private LocalFileHeader readLocalFileHeader() throws IOException {
|
||||
byte[] basicHeaderBytes = readHeader();
|
||||
if (basicHeaderBytes == null)
|
||||
return null;
|
||||
DataInputStream basicHeader = new DataInputStream(new ByteArrayInputStream(basicHeaderBytes));
|
||||
int firstHeaderSize = basicHeader.readUnsignedByte();
|
||||
byte[] firstHeaderBytes = new byte[firstHeaderSize - 1];
|
||||
basicHeader.readFully(firstHeaderBytes);
|
||||
DataInputStream firstHeader = new DataInputStream(new ByteArrayInputStream(firstHeaderBytes));
|
||||
LocalFileHeader localFileHeader = new LocalFileHeader();
|
||||
localFileHeader.archiverVersionNumber = firstHeader.readUnsignedByte();
|
||||
localFileHeader.minVersionToExtract = firstHeader.readUnsignedByte();
|
||||
localFileHeader.hostOS = firstHeader.readUnsignedByte();
|
||||
localFileHeader.arjFlags = firstHeader.readUnsignedByte();
|
||||
localFileHeader.method = firstHeader.readUnsignedByte();
|
||||
localFileHeader.fileType = firstHeader.readUnsignedByte();
|
||||
localFileHeader.reserved = firstHeader.readUnsignedByte();
|
||||
localFileHeader.dateTimeModified = read32(firstHeader);
|
||||
localFileHeader.compressedSize = 0xFFFFFFFFL & (long)read32(firstHeader);
|
||||
localFileHeader.originalSize = 0xFFFFFFFFL & (long)read32(firstHeader);
|
||||
localFileHeader.originalCrc32 = 0xFFFFFFFFL & (long)read32(firstHeader);
|
||||
localFileHeader.fileSpecPosition = read16(firstHeader);
|
||||
localFileHeader.fileAccessMode = read16(firstHeader);
|
||||
pushedBackBytes(20L);
|
||||
localFileHeader.firstChapter = firstHeader.readUnsignedByte();
|
||||
localFileHeader.lastChapter = firstHeader.readUnsignedByte();
|
||||
readExtraData(firstHeaderSize, firstHeader, localFileHeader);
|
||||
localFileHeader.name = readString(basicHeader);
|
||||
localFileHeader.comment = readString(basicHeader);
|
||||
ArrayList<byte[]> extendedHeaders = new ArrayList<byte[]>();
|
||||
int extendedHeaderSize;
|
||||
while ((extendedHeaderSize = read16(this.in)) > 0) {
|
||||
byte[] extendedHeaderBytes = new byte[extendedHeaderSize];
|
||||
readFully(this.in, extendedHeaderBytes);
|
||||
long extendedHeaderCrc32 = 0xFFFFFFFFL & (long)read32(this.in);
|
||||
CRC32 crc32 = new CRC32();
|
||||
crc32.update(extendedHeaderBytes);
|
||||
if (extendedHeaderCrc32 != crc32.getValue())
|
||||
throw new IOException("Extended header CRC32 verification failure");
|
||||
extendedHeaders.add(extendedHeaderBytes);
|
||||
}
|
||||
localFileHeader.extendedHeaders = extendedHeaders.<byte[]>toArray(new byte[extendedHeaders.size()][]);
|
||||
return localFileHeader;
|
||||
}
|
||||
|
||||
private void readExtraData(int firstHeaderSize, DataInputStream firstHeader, LocalFileHeader localFileHeader) throws IOException {
|
||||
if (firstHeaderSize >= 33) {
|
||||
localFileHeader.extendedFilePosition = read32(firstHeader);
|
||||
if (firstHeaderSize >= 45) {
|
||||
localFileHeader.dateTimeAccessed = read32(firstHeader);
|
||||
localFileHeader.dateTimeCreated = read32(firstHeader);
|
||||
localFileHeader.originalSizeEvenForVolumes = read32(firstHeader);
|
||||
pushedBackBytes(12L);
|
||||
}
|
||||
pushedBackBytes(4L);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean matches(byte[] signature, int length) {
|
||||
return (length >= 2 && (0xFF & signature[0]) == 96 && (0xFF & signature[1]) == 234);
|
||||
}
|
||||
|
||||
public String getArchiveName() {
|
||||
return this.mainHeader.name;
|
||||
}
|
||||
|
||||
public String getArchiveComment() {
|
||||
return this.mainHeader.comment;
|
||||
}
|
||||
|
||||
public ArjArchiveEntry getNextEntry() throws IOException {
|
||||
if (this.currentInputStream != null) {
|
||||
IOUtils.skip(this.currentInputStream, Long.MAX_VALUE);
|
||||
this.currentInputStream.close();
|
||||
this.currentLocalFileHeader = null;
|
||||
this.currentInputStream = null;
|
||||
}
|
||||
this.currentLocalFileHeader = readLocalFileHeader();
|
||||
if (this.currentLocalFileHeader != null) {
|
||||
this.currentInputStream = new BoundedInputStream(this.in, this.currentLocalFileHeader.compressedSize);
|
||||
if (this.currentLocalFileHeader.method == 0)
|
||||
this.currentInputStream = new CRC32VerifyingInputStream(this.currentInputStream, this.currentLocalFileHeader.originalSize, this.currentLocalFileHeader.originalCrc32);
|
||||
return new ArjArchiveEntry(this.currentLocalFileHeader);
|
||||
}
|
||||
this.currentInputStream = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean canReadEntryData(ArchiveEntry ae) {
|
||||
return (ae instanceof ArjArchiveEntry && ((ArjArchiveEntry)ae).getMethod() == 0);
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
if (this.currentLocalFileHeader == null)
|
||||
throw new IllegalStateException("No current arj entry");
|
||||
if (this.currentLocalFileHeader.method != 0)
|
||||
throw new IOException("Unsupported compression method " + this.currentLocalFileHeader.method);
|
||||
return this.currentInputStream.read(b, off, len);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
package org.apache.commons.compress.archivers.arj;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
class LocalFileHeader {
|
||||
int archiverVersionNumber;
|
||||
|
||||
int minVersionToExtract;
|
||||
|
||||
int hostOS;
|
||||
|
||||
int arjFlags;
|
||||
|
||||
int method;
|
||||
|
||||
int fileType;
|
||||
|
||||
int reserved;
|
||||
|
||||
int dateTimeModified;
|
||||
|
||||
long compressedSize;
|
||||
|
||||
long originalSize;
|
||||
|
||||
long originalCrc32;
|
||||
|
||||
int fileSpecPosition;
|
||||
|
||||
int fileAccessMode;
|
||||
|
||||
int firstChapter;
|
||||
|
||||
int lastChapter;
|
||||
|
||||
int extendedFilePosition;
|
||||
|
||||
int dateTimeAccessed;
|
||||
|
||||
int dateTimeCreated;
|
||||
|
||||
int originalSizeEvenForVolumes;
|
||||
|
||||
String name;
|
||||
|
||||
String comment;
|
||||
|
||||
byte[][] extendedHeaders = null;
|
||||
|
||||
static class Flags {
|
||||
static final int GARBLED = 1;
|
||||
|
||||
static final int VOLUME = 4;
|
||||
|
||||
static final int EXTFILE = 8;
|
||||
|
||||
static final int PATHSYM = 16;
|
||||
|
||||
static final int BACKUP = 32;
|
||||
}
|
||||
|
||||
static class FileTypes {
|
||||
static final int BINARY = 0;
|
||||
|
||||
static final int SEVEN_BIT_TEXT = 1;
|
||||
|
||||
static final int DIRECTORY = 3;
|
||||
|
||||
static final int VOLUME_LABEL = 4;
|
||||
|
||||
static final int CHAPTER_LABEL = 5;
|
||||
}
|
||||
|
||||
static class Methods {
|
||||
static final int STORED = 0;
|
||||
|
||||
static final int COMPRESSED_MOST = 1;
|
||||
|
||||
static final int COMPRESSED_FASTEST = 4;
|
||||
|
||||
static final int NO_DATA_NO_CRC = 8;
|
||||
|
||||
static final int NO_DATA = 9;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("LocalFileHeader [archiverVersionNumber=");
|
||||
builder.append(this.archiverVersionNumber);
|
||||
builder.append(", minVersionToExtract=");
|
||||
builder.append(this.minVersionToExtract);
|
||||
builder.append(", hostOS=");
|
||||
builder.append(this.hostOS);
|
||||
builder.append(", arjFlags=");
|
||||
builder.append(this.arjFlags);
|
||||
builder.append(", method=");
|
||||
builder.append(this.method);
|
||||
builder.append(", fileType=");
|
||||
builder.append(this.fileType);
|
||||
builder.append(", reserved=");
|
||||
builder.append(this.reserved);
|
||||
builder.append(", dateTimeModified=");
|
||||
builder.append(this.dateTimeModified);
|
||||
builder.append(", compressedSize=");
|
||||
builder.append(this.compressedSize);
|
||||
builder.append(", originalSize=");
|
||||
builder.append(this.originalSize);
|
||||
builder.append(", originalCrc32=");
|
||||
builder.append(this.originalCrc32);
|
||||
builder.append(", fileSpecPosition=");
|
||||
builder.append(this.fileSpecPosition);
|
||||
builder.append(", fileAccessMode=");
|
||||
builder.append(this.fileAccessMode);
|
||||
builder.append(", firstChapter=");
|
||||
builder.append(this.firstChapter);
|
||||
builder.append(", lastChapter=");
|
||||
builder.append(this.lastChapter);
|
||||
builder.append(", extendedFilePosition=");
|
||||
builder.append(this.extendedFilePosition);
|
||||
builder.append(", dateTimeAccessed=");
|
||||
builder.append(this.dateTimeAccessed);
|
||||
builder.append(", dateTimeCreated=");
|
||||
builder.append(this.dateTimeCreated);
|
||||
builder.append(", originalSizeEvenForVolumes=");
|
||||
builder.append(this.originalSizeEvenForVolumes);
|
||||
builder.append(", name=");
|
||||
builder.append(this.name);
|
||||
builder.append(", comment=");
|
||||
builder.append(this.comment);
|
||||
builder.append(", extendedHeaders=");
|
||||
builder.append(Arrays.toString((Object[])this.extendedHeaders));
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
package org.apache.commons.compress.archivers.arj;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
class MainHeader {
|
||||
int archiverVersionNumber;
|
||||
|
||||
int minVersionToExtract;
|
||||
|
||||
int hostOS;
|
||||
|
||||
int arjFlags;
|
||||
|
||||
int securityVersion;
|
||||
|
||||
int fileType;
|
||||
|
||||
int reserved;
|
||||
|
||||
int dateTimeCreated;
|
||||
|
||||
int dateTimeModified;
|
||||
|
||||
long archiveSize;
|
||||
|
||||
int securityEnvelopeFilePosition;
|
||||
|
||||
int fileSpecPosition;
|
||||
|
||||
int securityEnvelopeLength;
|
||||
|
||||
int encryptionVersion;
|
||||
|
||||
int lastChapter;
|
||||
|
||||
int arjProtectionFactor;
|
||||
|
||||
int arjFlags2;
|
||||
|
||||
String name;
|
||||
|
||||
String comment;
|
||||
|
||||
byte[] extendedHeaderBytes = null;
|
||||
|
||||
static class Flags {
|
||||
static final int GARBLED = 1;
|
||||
|
||||
static final int OLD_SECURED_NEW_ANSI_PAGE = 2;
|
||||
|
||||
static final int VOLUME = 4;
|
||||
|
||||
static final int ARJPROT = 8;
|
||||
|
||||
static final int PATHSYM = 16;
|
||||
|
||||
static final int BACKUP = 32;
|
||||
|
||||
static final int SECURED = 64;
|
||||
|
||||
static final int ALTNAME = 128;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("MainHeader [archiverVersionNumber=");
|
||||
builder.append(this.archiverVersionNumber);
|
||||
builder.append(", minVersionToExtract=");
|
||||
builder.append(this.minVersionToExtract);
|
||||
builder.append(", hostOS=");
|
||||
builder.append(this.hostOS);
|
||||
builder.append(", arjFlags=");
|
||||
builder.append(this.arjFlags);
|
||||
builder.append(", securityVersion=");
|
||||
builder.append(this.securityVersion);
|
||||
builder.append(", fileType=");
|
||||
builder.append(this.fileType);
|
||||
builder.append(", reserved=");
|
||||
builder.append(this.reserved);
|
||||
builder.append(", dateTimeCreated=");
|
||||
builder.append(this.dateTimeCreated);
|
||||
builder.append(", dateTimeModified=");
|
||||
builder.append(this.dateTimeModified);
|
||||
builder.append(", archiveSize=");
|
||||
builder.append(this.archiveSize);
|
||||
builder.append(", securityEnvelopeFilePosition=");
|
||||
builder.append(this.securityEnvelopeFilePosition);
|
||||
builder.append(", fileSpecPosition=");
|
||||
builder.append(this.fileSpecPosition);
|
||||
builder.append(", securityEnvelopeLength=");
|
||||
builder.append(this.securityEnvelopeLength);
|
||||
builder.append(", encryptionVersion=");
|
||||
builder.append(this.encryptionVersion);
|
||||
builder.append(", lastChapter=");
|
||||
builder.append(this.lastChapter);
|
||||
builder.append(", arjProtectionFactor=");
|
||||
builder.append(this.arjProtectionFactor);
|
||||
builder.append(", arjFlags2=");
|
||||
builder.append(this.arjFlags2);
|
||||
builder.append(", name=");
|
||||
builder.append(this.name);
|
||||
builder.append(", comment=");
|
||||
builder.append(this.comment);
|
||||
builder.append(", extendedHeaderBytes=");
|
||||
builder.append(Arrays.toString(this.extendedHeaderBytes));
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,350 @@
|
|||
package org.apache.commons.compress.archivers.cpio;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
|
||||
public class CpioArchiveEntry implements CpioConstants, ArchiveEntry {
|
||||
private final short fileFormat;
|
||||
|
||||
private final int headerSize;
|
||||
|
||||
private final int alignmentBoundary;
|
||||
|
||||
private long chksum = 0L;
|
||||
|
||||
private long filesize = 0L;
|
||||
|
||||
private long gid = 0L;
|
||||
|
||||
private long inode = 0L;
|
||||
|
||||
private long maj = 0L;
|
||||
|
||||
private long min = 0L;
|
||||
|
||||
private long mode = 0L;
|
||||
|
||||
private long mtime = 0L;
|
||||
|
||||
private String name;
|
||||
|
||||
private long nlink = 0L;
|
||||
|
||||
private long rmaj = 0L;
|
||||
|
||||
private long rmin = 0L;
|
||||
|
||||
private long uid = 0L;
|
||||
|
||||
public CpioArchiveEntry(short format) {
|
||||
switch (format) {
|
||||
case 1:
|
||||
this.headerSize = 110;
|
||||
this.alignmentBoundary = 4;
|
||||
break;
|
||||
case 2:
|
||||
this.headerSize = 110;
|
||||
this.alignmentBoundary = 4;
|
||||
break;
|
||||
case 4:
|
||||
this.headerSize = 76;
|
||||
this.alignmentBoundary = 0;
|
||||
break;
|
||||
case 8:
|
||||
this.headerSize = 26;
|
||||
this.alignmentBoundary = 2;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown header type");
|
||||
}
|
||||
this.fileFormat = format;
|
||||
}
|
||||
|
||||
public CpioArchiveEntry(String name) {
|
||||
this((short)1, name);
|
||||
}
|
||||
|
||||
public CpioArchiveEntry(short format, String name) {
|
||||
this(format);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public CpioArchiveEntry(String name, long size) {
|
||||
this(name);
|
||||
setSize(size);
|
||||
}
|
||||
|
||||
public CpioArchiveEntry(short format, String name, long size) {
|
||||
this(format, name);
|
||||
setSize(size);
|
||||
}
|
||||
|
||||
public CpioArchiveEntry(File inputFile, String entryName) {
|
||||
this((short)1, inputFile, entryName);
|
||||
}
|
||||
|
||||
public CpioArchiveEntry(short format, File inputFile, String entryName) {
|
||||
this(format, entryName, inputFile.isFile() ? inputFile.length() : 0L);
|
||||
if (inputFile.isDirectory()) {
|
||||
setMode(16384L);
|
||||
} else if (inputFile.isFile()) {
|
||||
setMode(32768L);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Cannot determine type of file " + inputFile.getName());
|
||||
}
|
||||
setTime(inputFile.lastModified() / 1000L);
|
||||
}
|
||||
|
||||
private void checkNewFormat() {
|
||||
if ((this.fileFormat & 0x3) == 0)
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private void checkOldFormat() {
|
||||
if ((this.fileFormat & 0xC) == 0)
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public long getChksum() {
|
||||
checkNewFormat();
|
||||
return this.chksum;
|
||||
}
|
||||
|
||||
public long getDevice() {
|
||||
checkOldFormat();
|
||||
return this.min;
|
||||
}
|
||||
|
||||
public long getDeviceMaj() {
|
||||
checkNewFormat();
|
||||
return this.maj;
|
||||
}
|
||||
|
||||
public long getDeviceMin() {
|
||||
checkNewFormat();
|
||||
return this.min;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return this.filesize;
|
||||
}
|
||||
|
||||
public short getFormat() {
|
||||
return this.fileFormat;
|
||||
}
|
||||
|
||||
public long getGID() {
|
||||
return this.gid;
|
||||
}
|
||||
|
||||
public int getHeaderSize() {
|
||||
return this.headerSize;
|
||||
}
|
||||
|
||||
public int getAlignmentBoundary() {
|
||||
return this.alignmentBoundary;
|
||||
}
|
||||
|
||||
public int getHeaderPadCount() {
|
||||
if (this.alignmentBoundary == 0)
|
||||
return 0;
|
||||
int size = this.headerSize + 1;
|
||||
if (this.name != null)
|
||||
size += this.name.length();
|
||||
int remain = size % this.alignmentBoundary;
|
||||
if (remain > 0)
|
||||
return this.alignmentBoundary - remain;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getDataPadCount() {
|
||||
if (this.alignmentBoundary == 0)
|
||||
return 0;
|
||||
long size = this.filesize;
|
||||
int remain = (int)(size % (long)this.alignmentBoundary);
|
||||
if (remain > 0)
|
||||
return this.alignmentBoundary - remain;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long getInode() {
|
||||
return this.inode;
|
||||
}
|
||||
|
||||
public long getMode() {
|
||||
return (this.mode == 0L && !"TRAILER!!!".equals(this.name)) ? 32768L : this.mode;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public long getNumberOfLinks() {
|
||||
return (this.nlink == 0L) ? (isDirectory() ? 2L : 1L) : this.nlink;
|
||||
}
|
||||
|
||||
public long getRemoteDevice() {
|
||||
checkOldFormat();
|
||||
return this.rmin;
|
||||
}
|
||||
|
||||
public long getRemoteDeviceMaj() {
|
||||
checkNewFormat();
|
||||
return this.rmaj;
|
||||
}
|
||||
|
||||
public long getRemoteDeviceMin() {
|
||||
checkNewFormat();
|
||||
return this.rmin;
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return this.mtime;
|
||||
}
|
||||
|
||||
public Date getLastModifiedDate() {
|
||||
return new Date(1000L * getTime());
|
||||
}
|
||||
|
||||
public long getUID() {
|
||||
return this.uid;
|
||||
}
|
||||
|
||||
public boolean isBlockDevice() {
|
||||
return (CpioUtil.fileType(this.mode) == 24576L);
|
||||
}
|
||||
|
||||
public boolean isCharacterDevice() {
|
||||
return (CpioUtil.fileType(this.mode) == 8192L);
|
||||
}
|
||||
|
||||
public boolean isDirectory() {
|
||||
return (CpioUtil.fileType(this.mode) == 16384L);
|
||||
}
|
||||
|
||||
public boolean isNetwork() {
|
||||
return (CpioUtil.fileType(this.mode) == 36864L);
|
||||
}
|
||||
|
||||
public boolean isPipe() {
|
||||
return (CpioUtil.fileType(this.mode) == 4096L);
|
||||
}
|
||||
|
||||
public boolean isRegularFile() {
|
||||
return (CpioUtil.fileType(this.mode) == 32768L);
|
||||
}
|
||||
|
||||
public boolean isSocket() {
|
||||
return (CpioUtil.fileType(this.mode) == 49152L);
|
||||
}
|
||||
|
||||
public boolean isSymbolicLink() {
|
||||
return (CpioUtil.fileType(this.mode) == 40960L);
|
||||
}
|
||||
|
||||
public void setChksum(long chksum) {
|
||||
checkNewFormat();
|
||||
this.chksum = chksum;
|
||||
}
|
||||
|
||||
public void setDevice(long device) {
|
||||
checkOldFormat();
|
||||
this.min = device;
|
||||
}
|
||||
|
||||
public void setDeviceMaj(long maj) {
|
||||
checkNewFormat();
|
||||
this.maj = maj;
|
||||
}
|
||||
|
||||
public void setDeviceMin(long min) {
|
||||
checkNewFormat();
|
||||
this.min = min;
|
||||
}
|
||||
|
||||
public void setSize(long size) {
|
||||
if (size < 0L || size > 4294967295L)
|
||||
throw new IllegalArgumentException("invalid entry size <" + size + ">");
|
||||
this.filesize = size;
|
||||
}
|
||||
|
||||
public void setGID(long gid) {
|
||||
this.gid = gid;
|
||||
}
|
||||
|
||||
public void setInode(long inode) {
|
||||
this.inode = inode;
|
||||
}
|
||||
|
||||
public void setMode(long mode) {
|
||||
long maskedMode = mode & 0xF000L;
|
||||
switch ((int)maskedMode) {
|
||||
case 4096:
|
||||
case 8192:
|
||||
case 16384:
|
||||
case 24576:
|
||||
case 32768:
|
||||
case 36864:
|
||||
case 40960:
|
||||
case 49152:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown mode. Full: " + Long.toHexString(mode) + " Masked: " + Long.toHexString(maskedMode));
|
||||
}
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setNumberOfLinks(long nlink) {
|
||||
this.nlink = nlink;
|
||||
}
|
||||
|
||||
public void setRemoteDevice(long device) {
|
||||
checkOldFormat();
|
||||
this.rmin = device;
|
||||
}
|
||||
|
||||
public void setRemoteDeviceMaj(long rmaj) {
|
||||
checkNewFormat();
|
||||
this.rmaj = rmaj;
|
||||
}
|
||||
|
||||
public void setRemoteDeviceMin(long rmin) {
|
||||
checkNewFormat();
|
||||
this.rmin = rmin;
|
||||
}
|
||||
|
||||
public void setTime(long time) {
|
||||
this.mtime = time;
|
||||
}
|
||||
|
||||
public void setUID(long uid) {
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int prime = 31;
|
||||
int result = 1;
|
||||
result = 31 * result + ((this.name == null) ? 0 : this.name.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
CpioArchiveEntry other = (CpioArchiveEntry)obj;
|
||||
if (this.name == null) {
|
||||
if (other.name != null)
|
||||
return false;
|
||||
} else if (!this.name.equals(other.name)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,304 @@
|
|||
package org.apache.commons.compress.archivers.cpio;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.ArchiveInputStream;
|
||||
import org.apache.commons.compress.archivers.zip.ZipEncoding;
|
||||
import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
|
||||
import org.apache.commons.compress.utils.ArchiveUtils;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
|
||||
public class CpioArchiveInputStream extends ArchiveInputStream implements CpioConstants {
|
||||
private boolean closed = false;
|
||||
|
||||
private CpioArchiveEntry entry;
|
||||
|
||||
private long entryBytesRead = 0L;
|
||||
|
||||
private boolean entryEOF = false;
|
||||
|
||||
private final byte[] tmpbuf = new byte[4096];
|
||||
|
||||
private long crc = 0L;
|
||||
|
||||
private final InputStream in;
|
||||
|
||||
private final byte[] TWO_BYTES_BUF = new byte[2];
|
||||
|
||||
private final byte[] FOUR_BYTES_BUF = new byte[4];
|
||||
|
||||
private final byte[] SIX_BYTES_BUF = new byte[6];
|
||||
|
||||
private final int blockSize;
|
||||
|
||||
private final ZipEncoding encoding;
|
||||
|
||||
public CpioArchiveInputStream(InputStream in) {
|
||||
this(in, 512, "US-ASCII");
|
||||
}
|
||||
|
||||
public CpioArchiveInputStream(InputStream in, String encoding) {
|
||||
this(in, 512, encoding);
|
||||
}
|
||||
|
||||
public CpioArchiveInputStream(InputStream in, int blockSize) {
|
||||
this(in, blockSize, "US-ASCII");
|
||||
}
|
||||
|
||||
public CpioArchiveInputStream(InputStream in, int blockSize, String encoding) {
|
||||
this.in = in;
|
||||
this.blockSize = blockSize;
|
||||
this.encoding = ZipEncodingHelper.getZipEncoding(encoding);
|
||||
}
|
||||
|
||||
public int available() throws IOException {
|
||||
ensureOpen();
|
||||
if (this.entryEOF)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (!this.closed) {
|
||||
this.in.close();
|
||||
this.closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void closeEntry() throws IOException {
|
||||
while (skip(Integer.MAX_VALUE) == Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
private void ensureOpen() throws IOException {
|
||||
if (this.closed)
|
||||
throw new IOException("Stream closed");
|
||||
}
|
||||
|
||||
public CpioArchiveEntry getNextCPIOEntry() throws IOException {
|
||||
ensureOpen();
|
||||
if (this.entry != null)
|
||||
closeEntry();
|
||||
readFully(this.TWO_BYTES_BUF, 0, this.TWO_BYTES_BUF.length);
|
||||
if (CpioUtil.byteArray2long(this.TWO_BYTES_BUF, false) == 29127L) {
|
||||
this.entry = readOldBinaryEntry(false);
|
||||
} else if (CpioUtil.byteArray2long(this.TWO_BYTES_BUF, true) == 29127L) {
|
||||
this.entry = readOldBinaryEntry(true);
|
||||
} else {
|
||||
System.arraycopy(this.TWO_BYTES_BUF, 0, this.SIX_BYTES_BUF, 0, this.TWO_BYTES_BUF.length);
|
||||
readFully(this.SIX_BYTES_BUF, this.TWO_BYTES_BUF.length, this.FOUR_BYTES_BUF.length);
|
||||
String magicString = ArchiveUtils.toAsciiString(this.SIX_BYTES_BUF);
|
||||
if (magicString.equals("070701")) {
|
||||
this.entry = readNewEntry(false);
|
||||
} else if (magicString.equals("070702")) {
|
||||
this.entry = readNewEntry(true);
|
||||
} else if (magicString.equals("070707")) {
|
||||
this.entry = readOldAsciiEntry();
|
||||
} else {
|
||||
throw new IOException("Unknown magic [" + magicString + "]. Occured at byte: " + getBytesRead());
|
||||
}
|
||||
}
|
||||
this.entryBytesRead = 0L;
|
||||
this.entryEOF = false;
|
||||
this.crc = 0L;
|
||||
if (this.entry.getName().equals("TRAILER!!!")) {
|
||||
this.entryEOF = true;
|
||||
skipRemainderOfLastBlock();
|
||||
return null;
|
||||
}
|
||||
return this.entry;
|
||||
}
|
||||
|
||||
private void skip(int bytes) throws IOException {
|
||||
if (bytes > 0)
|
||||
readFully(this.FOUR_BYTES_BUF, 0, bytes);
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
ensureOpen();
|
||||
if (off < 0 || len < 0 || off > b.length - len)
|
||||
throw new IndexOutOfBoundsException();
|
||||
if (len == 0)
|
||||
return 0;
|
||||
if (this.entry == null || this.entryEOF)
|
||||
return -1;
|
||||
if (this.entryBytesRead == this.entry.getSize()) {
|
||||
skip(this.entry.getDataPadCount());
|
||||
this.entryEOF = true;
|
||||
if (this.entry.getFormat() == 2 && this.crc != this.entry.getChksum())
|
||||
throw new IOException("CRC Error. Occured at byte: " + getBytesRead());
|
||||
return -1;
|
||||
}
|
||||
int tmplength = (int)Math.min((long)len, this.entry.getSize() - this.entryBytesRead);
|
||||
if (tmplength < 0)
|
||||
return -1;
|
||||
int tmpread = readFully(b, off, tmplength);
|
||||
if (this.entry.getFormat() == 2)
|
||||
for (int pos = 0; pos < tmpread; pos++)
|
||||
this.crc += (long)(b[pos] & 0xFF);
|
||||
this.entryBytesRead += (long)tmpread;
|
||||
return tmpread;
|
||||
}
|
||||
|
||||
private final int readFully(byte[] b, int off, int len) throws IOException {
|
||||
int count = IOUtils.readFully(this.in, b, off, len);
|
||||
count(count);
|
||||
if (count < len)
|
||||
throw new EOFException();
|
||||
return count;
|
||||
}
|
||||
|
||||
private long readBinaryLong(int length, boolean swapHalfWord) throws IOException {
|
||||
byte[] tmp = new byte[length];
|
||||
readFully(tmp, 0, tmp.length);
|
||||
return CpioUtil.byteArray2long(tmp, swapHalfWord);
|
||||
}
|
||||
|
||||
private long readAsciiLong(int length, int radix) throws IOException {
|
||||
byte[] tmpBuffer = new byte[length];
|
||||
readFully(tmpBuffer, 0, tmpBuffer.length);
|
||||
return Long.parseLong(ArchiveUtils.toAsciiString(tmpBuffer), radix);
|
||||
}
|
||||
|
||||
private CpioArchiveEntry readNewEntry(boolean hasCrc) throws IOException {
|
||||
CpioArchiveEntry ret;
|
||||
if (hasCrc) {
|
||||
ret = new CpioArchiveEntry((short)2);
|
||||
} else {
|
||||
ret = new CpioArchiveEntry((short)1);
|
||||
}
|
||||
ret.setInode(readAsciiLong(8, 16));
|
||||
long mode = readAsciiLong(8, 16);
|
||||
if (CpioUtil.fileType(mode) != 0L)
|
||||
ret.setMode(mode);
|
||||
ret.setUID(readAsciiLong(8, 16));
|
||||
ret.setGID(readAsciiLong(8, 16));
|
||||
ret.setNumberOfLinks(readAsciiLong(8, 16));
|
||||
ret.setTime(readAsciiLong(8, 16));
|
||||
ret.setSize(readAsciiLong(8, 16));
|
||||
ret.setDeviceMaj(readAsciiLong(8, 16));
|
||||
ret.setDeviceMin(readAsciiLong(8, 16));
|
||||
ret.setRemoteDeviceMaj(readAsciiLong(8, 16));
|
||||
ret.setRemoteDeviceMin(readAsciiLong(8, 16));
|
||||
long namesize = readAsciiLong(8, 16);
|
||||
ret.setChksum(readAsciiLong(8, 16));
|
||||
String name = readCString((int)namesize);
|
||||
ret.setName(name);
|
||||
if (CpioUtil.fileType(mode) == 0L && !name.equals("TRAILER!!!"))
|
||||
throw new IOException("Mode 0 only allowed in the trailer. Found entry name: " + name + " Occured at byte: " + getBytesRead());
|
||||
skip(ret.getHeaderPadCount());
|
||||
return ret;
|
||||
}
|
||||
|
||||
private CpioArchiveEntry readOldAsciiEntry() throws IOException {
|
||||
CpioArchiveEntry ret = new CpioArchiveEntry((short)4);
|
||||
ret.setDevice(readAsciiLong(6, 8));
|
||||
ret.setInode(readAsciiLong(6, 8));
|
||||
long mode = readAsciiLong(6, 8);
|
||||
if (CpioUtil.fileType(mode) != 0L)
|
||||
ret.setMode(mode);
|
||||
ret.setUID(readAsciiLong(6, 8));
|
||||
ret.setGID(readAsciiLong(6, 8));
|
||||
ret.setNumberOfLinks(readAsciiLong(6, 8));
|
||||
ret.setRemoteDevice(readAsciiLong(6, 8));
|
||||
ret.setTime(readAsciiLong(11, 8));
|
||||
long namesize = readAsciiLong(6, 8);
|
||||
ret.setSize(readAsciiLong(11, 8));
|
||||
String name = readCString((int)namesize);
|
||||
ret.setName(name);
|
||||
if (CpioUtil.fileType(mode) == 0L && !name.equals("TRAILER!!!"))
|
||||
throw new IOException("Mode 0 only allowed in the trailer. Found entry: " + name + " Occured at byte: " + getBytesRead());
|
||||
return ret;
|
||||
}
|
||||
|
||||
private CpioArchiveEntry readOldBinaryEntry(boolean swapHalfWord) throws IOException {
|
||||
CpioArchiveEntry ret = new CpioArchiveEntry((short)8);
|
||||
ret.setDevice(readBinaryLong(2, swapHalfWord));
|
||||
ret.setInode(readBinaryLong(2, swapHalfWord));
|
||||
long mode = readBinaryLong(2, swapHalfWord);
|
||||
if (CpioUtil.fileType(mode) != 0L)
|
||||
ret.setMode(mode);
|
||||
ret.setUID(readBinaryLong(2, swapHalfWord));
|
||||
ret.setGID(readBinaryLong(2, swapHalfWord));
|
||||
ret.setNumberOfLinks(readBinaryLong(2, swapHalfWord));
|
||||
ret.setRemoteDevice(readBinaryLong(2, swapHalfWord));
|
||||
ret.setTime(readBinaryLong(4, swapHalfWord));
|
||||
long namesize = readBinaryLong(2, swapHalfWord);
|
||||
ret.setSize(readBinaryLong(4, swapHalfWord));
|
||||
String name = readCString((int)namesize);
|
||||
ret.setName(name);
|
||||
if (CpioUtil.fileType(mode) == 0L && !name.equals("TRAILER!!!"))
|
||||
throw new IOException("Mode 0 only allowed in the trailer. Found entry: " + name + "Occured at byte: " + getBytesRead());
|
||||
skip(ret.getHeaderPadCount());
|
||||
return ret;
|
||||
}
|
||||
|
||||
private String readCString(int length) throws IOException {
|
||||
byte[] tmpBuffer = new byte[length - 1];
|
||||
readFully(tmpBuffer, 0, tmpBuffer.length);
|
||||
this.in.read();
|
||||
return this.encoding.decode(tmpBuffer);
|
||||
}
|
||||
|
||||
public long skip(long n) throws IOException {
|
||||
if (n < 0L)
|
||||
throw new IllegalArgumentException("negative skip length");
|
||||
ensureOpen();
|
||||
int max = (int)Math.min(n, Integer.MAX_VALUE);
|
||||
int total = 0;
|
||||
while (total < max) {
|
||||
int len = max - total;
|
||||
if (len > this.tmpbuf.length)
|
||||
len = this.tmpbuf.length;
|
||||
len = read(this.tmpbuf, 0, len);
|
||||
if (len == -1) {
|
||||
this.entryEOF = true;
|
||||
break;
|
||||
}
|
||||
total += len;
|
||||
}
|
||||
return (long)total;
|
||||
}
|
||||
|
||||
public ArchiveEntry getNextEntry() throws IOException {
|
||||
return getNextCPIOEntry();
|
||||
}
|
||||
|
||||
private void skipRemainderOfLastBlock() throws IOException {
|
||||
long readFromLastBlock = getBytesRead() % (long)this.blockSize;
|
||||
long remainingBytes = (readFromLastBlock == 0L) ? 0L : ((long)this.blockSize - readFromLastBlock);
|
||||
while (remainingBytes > 0L) {
|
||||
long skipped = skip((long)this.blockSize - readFromLastBlock);
|
||||
if (skipped <= 0L)
|
||||
break;
|
||||
remainingBytes -= skipped;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean matches(byte[] signature, int length) {
|
||||
if (length < 6)
|
||||
return false;
|
||||
if (signature[0] == 113 && (signature[1] & 0xFF) == 199)
|
||||
return true;
|
||||
if (signature[1] == 113 && (signature[0] & 0xFF) == 199)
|
||||
return true;
|
||||
if (signature[0] != 48)
|
||||
return false;
|
||||
if (signature[1] != 55)
|
||||
return false;
|
||||
if (signature[2] != 48)
|
||||
return false;
|
||||
if (signature[3] != 55)
|
||||
return false;
|
||||
if (signature[4] != 48)
|
||||
return false;
|
||||
if (signature[5] == 49)
|
||||
return true;
|
||||
if (signature[5] == 50)
|
||||
return true;
|
||||
if (signature[5] == 55)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,296 @@
|
|||
package org.apache.commons.compress.archivers.cpio;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.ArchiveOutputStream;
|
||||
import org.apache.commons.compress.archivers.zip.ZipEncoding;
|
||||
import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
|
||||
import org.apache.commons.compress.utils.ArchiveUtils;
|
||||
|
||||
public class CpioArchiveOutputStream extends ArchiveOutputStream implements CpioConstants {
|
||||
private CpioArchiveEntry entry;
|
||||
|
||||
private boolean closed = false;
|
||||
|
||||
private boolean finished;
|
||||
|
||||
private final short entryFormat;
|
||||
|
||||
private final HashMap<String, CpioArchiveEntry> names = new HashMap<String, CpioArchiveEntry>();
|
||||
|
||||
private long crc = 0L;
|
||||
|
||||
private long written;
|
||||
|
||||
private final OutputStream out;
|
||||
|
||||
private final int blockSize;
|
||||
|
||||
private long nextArtificalDeviceAndInode = 1L;
|
||||
|
||||
private final ZipEncoding encoding;
|
||||
|
||||
public CpioArchiveOutputStream(OutputStream out, short format) {
|
||||
this(out, format, 512, "US-ASCII");
|
||||
}
|
||||
|
||||
public CpioArchiveOutputStream(OutputStream out, short format, int blockSize) {
|
||||
this(out, format, blockSize, "US-ASCII");
|
||||
}
|
||||
|
||||
public CpioArchiveOutputStream(OutputStream out, short format, int blockSize, String encoding) {
|
||||
this.out = out;
|
||||
switch (format) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown format: " + format);
|
||||
}
|
||||
this.entryFormat = format;
|
||||
this.blockSize = blockSize;
|
||||
this.encoding = ZipEncodingHelper.getZipEncoding(encoding);
|
||||
}
|
||||
|
||||
public CpioArchiveOutputStream(OutputStream out) {
|
||||
this(out, (short)1);
|
||||
}
|
||||
|
||||
public CpioArchiveOutputStream(OutputStream out, String encoding) {
|
||||
this(out, (short)1, 512, encoding);
|
||||
}
|
||||
|
||||
private void ensureOpen() throws IOException {
|
||||
if (this.closed)
|
||||
throw new IOException("Stream closed");
|
||||
}
|
||||
|
||||
public void putArchiveEntry(ArchiveEntry entry) throws IOException {
|
||||
if (this.finished)
|
||||
throw new IOException("Stream has already been finished");
|
||||
CpioArchiveEntry e = (CpioArchiveEntry)entry;
|
||||
ensureOpen();
|
||||
if (this.entry != null)
|
||||
closeArchiveEntry();
|
||||
if (e.getTime() == -1L)
|
||||
e.setTime(System.currentTimeMillis() / 1000L);
|
||||
short format = e.getFormat();
|
||||
if (format != this.entryFormat)
|
||||
throw new IOException("Header format: " + format + " does not match existing format: " + this.entryFormat);
|
||||
if (this.names.put(e.getName(), e) != null)
|
||||
throw new IOException("duplicate entry: " + e.getName());
|
||||
writeHeader(e);
|
||||
this.entry = e;
|
||||
this.written = 0L;
|
||||
}
|
||||
|
||||
private void writeHeader(CpioArchiveEntry e) throws IOException {
|
||||
boolean swapHalfWord;
|
||||
switch (e.getFormat()) {
|
||||
case 1:
|
||||
this.out.write(ArchiveUtils.toAsciiBytes("070701"));
|
||||
count(6);
|
||||
writeNewEntry(e);
|
||||
break;
|
||||
case 2:
|
||||
this.out.write(ArchiveUtils.toAsciiBytes("070702"));
|
||||
count(6);
|
||||
writeNewEntry(e);
|
||||
break;
|
||||
case 4:
|
||||
this.out.write(ArchiveUtils.toAsciiBytes("070707"));
|
||||
count(6);
|
||||
writeOldAsciiEntry(e);
|
||||
break;
|
||||
case 8:
|
||||
swapHalfWord = true;
|
||||
writeBinaryLong(29127L, 2, swapHalfWord);
|
||||
writeOldBinaryEntry(e, swapHalfWord);
|
||||
break;
|
||||
default:
|
||||
throw new IOException("unknown format " + e.getFormat());
|
||||
}
|
||||
}
|
||||
|
||||
private void writeNewEntry(CpioArchiveEntry entry) throws IOException {
|
||||
long inode = entry.getInode();
|
||||
long devMin = entry.getDeviceMin();
|
||||
inode = devMin = 0L;
|
||||
inode = this.nextArtificalDeviceAndInode & 0xFFFFFFFFFFFFFFFFL;
|
||||
devMin = this.nextArtificalDeviceAndInode++ >> 32L & 0xFFFFFFFFFFFFFFFFL;
|
||||
this.nextArtificalDeviceAndInode = Math.max(this.nextArtificalDeviceAndInode, inode + 4294967296L * devMin) + 1L;
|
||||
writeAsciiLong(inode, 8, 16);
|
||||
writeAsciiLong(entry.getMode(), 8, 16);
|
||||
writeAsciiLong(entry.getUID(), 8, 16);
|
||||
writeAsciiLong(entry.getGID(), 8, 16);
|
||||
writeAsciiLong(entry.getNumberOfLinks(), 8, 16);
|
||||
writeAsciiLong(entry.getTime(), 8, 16);
|
||||
writeAsciiLong(entry.getSize(), 8, 16);
|
||||
writeAsciiLong(entry.getDeviceMaj(), 8, 16);
|
||||
writeAsciiLong(devMin, 8, 16);
|
||||
writeAsciiLong(entry.getRemoteDeviceMaj(), 8, 16);
|
||||
writeAsciiLong(entry.getRemoteDeviceMin(), 8, 16);
|
||||
writeAsciiLong((long)(entry.getName().length() + 1), 8, 16);
|
||||
writeAsciiLong(entry.getChksum(), 8, 16);
|
||||
writeCString(entry.getName());
|
||||
pad(entry.getHeaderPadCount());
|
||||
}
|
||||
|
||||
private void writeOldAsciiEntry(CpioArchiveEntry entry) throws IOException {
|
||||
long inode = entry.getInode();
|
||||
long device = entry.getDevice();
|
||||
inode = device = 0L;
|
||||
inode = this.nextArtificalDeviceAndInode & 0x3FFFFL;
|
||||
device = this.nextArtificalDeviceAndInode++ >> 18L & 0x3FFFFL;
|
||||
this.nextArtificalDeviceAndInode = Math.max(this.nextArtificalDeviceAndInode, inode + 262144L * device) + 1L;
|
||||
writeAsciiLong(device, 6, 8);
|
||||
writeAsciiLong(inode, 6, 8);
|
||||
writeAsciiLong(entry.getMode(), 6, 8);
|
||||
writeAsciiLong(entry.getUID(), 6, 8);
|
||||
writeAsciiLong(entry.getGID(), 6, 8);
|
||||
writeAsciiLong(entry.getNumberOfLinks(), 6, 8);
|
||||
writeAsciiLong(entry.getRemoteDevice(), 6, 8);
|
||||
writeAsciiLong(entry.getTime(), 11, 8);
|
||||
writeAsciiLong((long)(entry.getName().length() + 1), 6, 8);
|
||||
writeAsciiLong(entry.getSize(), 11, 8);
|
||||
writeCString(entry.getName());
|
||||
}
|
||||
|
||||
private void writeOldBinaryEntry(CpioArchiveEntry entry, boolean swapHalfWord) throws IOException {
|
||||
long inode = entry.getInode();
|
||||
long device = entry.getDevice();
|
||||
inode = device = 0L;
|
||||
inode = this.nextArtificalDeviceAndInode & 0xFFFFL;
|
||||
device = this.nextArtificalDeviceAndInode++ >> 16L & 0xFFFFL;
|
||||
this.nextArtificalDeviceAndInode = Math.max(this.nextArtificalDeviceAndInode, inode + 65536L * device) + 1L;
|
||||
writeBinaryLong(device, 2, swapHalfWord);
|
||||
writeBinaryLong(inode, 2, swapHalfWord);
|
||||
writeBinaryLong(entry.getMode(), 2, swapHalfWord);
|
||||
writeBinaryLong(entry.getUID(), 2, swapHalfWord);
|
||||
writeBinaryLong(entry.getGID(), 2, swapHalfWord);
|
||||
writeBinaryLong(entry.getNumberOfLinks(), 2, swapHalfWord);
|
||||
writeBinaryLong(entry.getRemoteDevice(), 2, swapHalfWord);
|
||||
writeBinaryLong(entry.getTime(), 4, swapHalfWord);
|
||||
writeBinaryLong((long)(entry.getName().length() + 1), 2, swapHalfWord);
|
||||
writeBinaryLong(entry.getSize(), 4, swapHalfWord);
|
||||
writeCString(entry.getName());
|
||||
pad(entry.getHeaderPadCount());
|
||||
}
|
||||
|
||||
public void closeArchiveEntry() throws IOException {
|
||||
if (this.finished)
|
||||
throw new IOException("Stream has already been finished");
|
||||
ensureOpen();
|
||||
if (this.entry == null)
|
||||
throw new IOException("Trying to close non-existent entry");
|
||||
if (this.entry.getSize() != this.written)
|
||||
throw new IOException("invalid entry size (expected " + this.entry.getSize() + " but got " + this.written + " bytes)");
|
||||
pad(this.entry.getDataPadCount());
|
||||
if (this.entry.getFormat() == 2 && this.crc != this.entry.getChksum())
|
||||
throw new IOException("CRC Error");
|
||||
this.entry = null;
|
||||
this.crc = 0L;
|
||||
this.written = 0L;
|
||||
}
|
||||
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
ensureOpen();
|
||||
if (off < 0 || len < 0 || off > b.length - len)
|
||||
throw new IndexOutOfBoundsException();
|
||||
if (len == 0)
|
||||
return;
|
||||
if (this.entry == null)
|
||||
throw new IOException("no current CPIO entry");
|
||||
if (this.written + (long)len > this.entry.getSize())
|
||||
throw new IOException("attempt to write past end of STORED entry");
|
||||
this.out.write(b, off, len);
|
||||
this.written += (long)len;
|
||||
if (this.entry.getFormat() == 2)
|
||||
for (int pos = 0; pos < len; pos++)
|
||||
this.crc += (long)(b[pos] & 0xFF);
|
||||
count(len);
|
||||
}
|
||||
|
||||
public void finish() throws IOException {
|
||||
ensureOpen();
|
||||
if (this.finished)
|
||||
throw new IOException("This archive has already been finished");
|
||||
if (this.entry != null)
|
||||
throw new IOException("This archive contains unclosed entries.");
|
||||
this.entry = new CpioArchiveEntry(this.entryFormat);
|
||||
this.entry.setName("TRAILER!!!");
|
||||
this.entry.setNumberOfLinks(1L);
|
||||
writeHeader(this.entry);
|
||||
closeArchiveEntry();
|
||||
int lengthOfLastBlock = (int)(getBytesWritten() % (long)this.blockSize);
|
||||
if (lengthOfLastBlock != 0)
|
||||
pad(this.blockSize - lengthOfLastBlock);
|
||||
this.finished = true;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (!this.finished)
|
||||
finish();
|
||||
if (!this.closed) {
|
||||
this.out.close();
|
||||
this.closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void pad(int count) throws IOException {
|
||||
if (count > 0) {
|
||||
byte[] buff = new byte[count];
|
||||
this.out.write(buff);
|
||||
count(count);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeBinaryLong(long number, int length, boolean swapHalfWord) throws IOException {
|
||||
byte[] tmp = CpioUtil.long2byteArray(number, length, swapHalfWord);
|
||||
this.out.write(tmp);
|
||||
count(tmp.length);
|
||||
}
|
||||
|
||||
private void writeAsciiLong(long number, int length, int radix) throws IOException {
|
||||
String tmpStr;
|
||||
StringBuilder tmp = new StringBuilder();
|
||||
if (radix == 16) {
|
||||
tmp.append(Long.toHexString(number));
|
||||
} else if (radix == 8) {
|
||||
tmp.append(Long.toOctalString(number));
|
||||
} else {
|
||||
tmp.append(Long.toString(number));
|
||||
}
|
||||
if (tmp.length() <= length) {
|
||||
long insertLength = (long)(length - tmp.length());
|
||||
for (int pos = 0; (long)pos < insertLength; pos++)
|
||||
tmp.insert(0, "0");
|
||||
tmpStr = tmp.toString();
|
||||
} else {
|
||||
tmpStr = tmp.substring(tmp.length() - length);
|
||||
}
|
||||
byte[] b = ArchiveUtils.toAsciiBytes(tmpStr);
|
||||
this.out.write(b);
|
||||
count(b.length);
|
||||
}
|
||||
|
||||
private void writeCString(String str) throws IOException {
|
||||
ByteBuffer buf = this.encoding.encode(str);
|
||||
int len = buf.limit() - buf.position();
|
||||
this.out.write(buf.array(), buf.arrayOffset(), len);
|
||||
this.out.write(0);
|
||||
count(len + 1);
|
||||
}
|
||||
|
||||
public ArchiveEntry createArchiveEntry(File inputFile, String entryName) throws IOException {
|
||||
if (this.finished)
|
||||
throw new IOException("Stream has already been finished");
|
||||
return new CpioArchiveEntry(inputFile, entryName);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package org.apache.commons.compress.archivers.cpio;
|
||||
|
||||
public interface CpioConstants {
|
||||
public static final String MAGIC_NEW = "070701";
|
||||
|
||||
public static final String MAGIC_NEW_CRC = "070702";
|
||||
|
||||
public static final String MAGIC_OLD_ASCII = "070707";
|
||||
|
||||
public static final int MAGIC_OLD_BINARY = 29127;
|
||||
|
||||
public static final short FORMAT_NEW = 1;
|
||||
|
||||
public static final short FORMAT_NEW_CRC = 2;
|
||||
|
||||
public static final short FORMAT_OLD_ASCII = 4;
|
||||
|
||||
public static final short FORMAT_OLD_BINARY = 8;
|
||||
|
||||
public static final short FORMAT_NEW_MASK = 3;
|
||||
|
||||
public static final short FORMAT_OLD_MASK = 12;
|
||||
|
||||
public static final int S_IFMT = 61440;
|
||||
|
||||
public static final int C_ISSOCK = 49152;
|
||||
|
||||
public static final int C_ISLNK = 40960;
|
||||
|
||||
public static final int C_ISNWK = 36864;
|
||||
|
||||
public static final int C_ISREG = 32768;
|
||||
|
||||
public static final int C_ISBLK = 24576;
|
||||
|
||||
public static final int C_ISDIR = 16384;
|
||||
|
||||
public static final int C_ISCHR = 8192;
|
||||
|
||||
public static final int C_ISFIFO = 4096;
|
||||
|
||||
public static final int C_ISUID = 2048;
|
||||
|
||||
public static final int C_ISGID = 1024;
|
||||
|
||||
public static final int C_ISVTX = 512;
|
||||
|
||||
public static final int C_IRUSR = 256;
|
||||
|
||||
public static final int C_IWUSR = 128;
|
||||
|
||||
public static final int C_IXUSR = 64;
|
||||
|
||||
public static final int C_IRGRP = 32;
|
||||
|
||||
public static final int C_IWGRP = 16;
|
||||
|
||||
public static final int C_IXGRP = 8;
|
||||
|
||||
public static final int C_IROTH = 4;
|
||||
|
||||
public static final int C_IWOTH = 2;
|
||||
|
||||
public static final int C_IXOTH = 1;
|
||||
|
||||
public static final String CPIO_TRAILER = "TRAILER!!!";
|
||||
|
||||
public static final int BLOCK_SIZE = 512;
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package org.apache.commons.compress.archivers.cpio;
|
||||
|
||||
class CpioUtil {
|
||||
static long fileType(long mode) {
|
||||
return mode & 0xF000L;
|
||||
}
|
||||
|
||||
static long byteArray2long(byte[] number, boolean swapHalfWord) {
|
||||
if (number.length % 2 != 0)
|
||||
throw new UnsupportedOperationException();
|
||||
long ret = 0L;
|
||||
int pos = 0;
|
||||
byte[] tmp_number = new byte[number.length];
|
||||
System.arraycopy(number, 0, tmp_number, 0, number.length);
|
||||
if (!swapHalfWord) {
|
||||
byte tmp = 0;
|
||||
for (int i = 0; i < tmp_number.length; i++) {
|
||||
tmp = tmp_number[i];
|
||||
tmp_number[i++] = tmp_number[i];
|
||||
tmp_number[i] = tmp;
|
||||
}
|
||||
}
|
||||
ret = (long)(tmp_number[0] & 0xFF);
|
||||
for (pos = 1; pos < tmp_number.length; pos++) {
|
||||
ret <<= 8L;
|
||||
ret |= (long)(tmp_number[pos] & 0xFF);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static byte[] long2byteArray(long number, int length, boolean swapHalfWord) {
|
||||
byte[] ret = new byte[length];
|
||||
int pos = 0;
|
||||
long tmp_number = 0L;
|
||||
if (length % 2 != 0 || length < 2)
|
||||
throw new UnsupportedOperationException();
|
||||
tmp_number = number;
|
||||
for (pos = length - 1; pos >= 0; pos--) {
|
||||
ret[pos] = (byte)(int)(tmp_number & 0xFFL);
|
||||
tmp_number >>= 8L;
|
||||
}
|
||||
if (!swapHalfWord) {
|
||||
byte tmp = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
tmp = ret[i];
|
||||
ret[i++] = ret[i];
|
||||
ret[i] = tmp;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package org.apache.commons.compress.archivers.dump;
|
||||
|
||||
class Dirent {
|
||||
private final int ino;
|
||||
|
||||
private final int parentIno;
|
||||
|
||||
private final int type;
|
||||
|
||||
private final String name;
|
||||
|
||||
Dirent(int ino, int parentIno, int type, String name) {
|
||||
this.ino = ino;
|
||||
this.parentIno = parentIno;
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
int getIno() {
|
||||
return this.ino;
|
||||
}
|
||||
|
||||
int getParentIno() {
|
||||
return this.parentIno;
|
||||
}
|
||||
|
||||
int getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.format("[%d]: %s", this.ino, this.name);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package org.apache.commons.compress.archivers.dump;
|
||||
|
||||
public final class DumpArchiveConstants {
|
||||
public static final int TP_SIZE = 1024;
|
||||
|
||||
public static final int NTREC = 10;
|
||||
|
||||
public static final int HIGH_DENSITY_NTREC = 32;
|
||||
|
||||
public static final int OFS_MAGIC = 60011;
|
||||
|
||||
public static final int NFS_MAGIC = 60012;
|
||||
|
||||
public static final int FS_UFS2_MAGIC = 424935705;
|
||||
|
||||
public static final int CHECKSUM = 84446;
|
||||
|
||||
public static final int LBLSIZE = 16;
|
||||
|
||||
public static final int NAMELEN = 64;
|
||||
|
||||
public enum SEGMENT_TYPE {
|
||||
TAPE(1),
|
||||
INODE(2),
|
||||
BITS(3),
|
||||
ADDR(4),
|
||||
END(5),
|
||||
CLRI(6);
|
||||
|
||||
int code;
|
||||
|
||||
SEGMENT_TYPE(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public static SEGMENT_TYPE find(int code) {
|
||||
for (SEGMENT_TYPE t : values()) {
|
||||
if (t.code == code)
|
||||
return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public enum COMPRESSION_TYPE {
|
||||
ZLIB(0),
|
||||
BZLIB(1),
|
||||
LZO(2);
|
||||
|
||||
int code;
|
||||
|
||||
COMPRESSION_TYPE(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public static COMPRESSION_TYPE find(int code) {
|
||||
for (COMPRESSION_TYPE t : values()) {
|
||||
if (t.code == code)
|
||||
return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,416 @@
|
|||
package org.apache.commons.compress.archivers.dump;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
|
||||
public class DumpArchiveEntry implements ArchiveEntry {
|
||||
private String name;
|
||||
|
||||
private TYPE type = TYPE.UNKNOWN;
|
||||
|
||||
private int mode;
|
||||
|
||||
private Set<PERMISSION> permissions = Collections.<PERMISSION>emptySet();
|
||||
|
||||
private long size;
|
||||
|
||||
private long atime;
|
||||
|
||||
private long mtime;
|
||||
|
||||
private int uid;
|
||||
|
||||
private int gid;
|
||||
|
||||
private final DumpArchiveSummary summary = null;
|
||||
|
||||
private final TapeSegmentHeader header = new TapeSegmentHeader();
|
||||
|
||||
private String simpleName;
|
||||
|
||||
private String originalName;
|
||||
|
||||
private int volume;
|
||||
|
||||
private long offset;
|
||||
|
||||
private int ino;
|
||||
|
||||
private int nlink;
|
||||
|
||||
private long ctime;
|
||||
|
||||
private int generation;
|
||||
|
||||
private boolean isDeleted;
|
||||
|
||||
public DumpArchiveEntry() {}
|
||||
|
||||
public DumpArchiveEntry(String name, String simpleName) {
|
||||
setName(name);
|
||||
this.simpleName = simpleName;
|
||||
}
|
||||
|
||||
protected DumpArchiveEntry(String name, String simpleName, int ino, TYPE type) {
|
||||
setType(type);
|
||||
setName(name);
|
||||
this.simpleName = simpleName;
|
||||
this.ino = ino;
|
||||
this.offset = 0L;
|
||||
}
|
||||
|
||||
public String getSimpleName() {
|
||||
return this.simpleName;
|
||||
}
|
||||
|
||||
protected void setSimpleName(String simpleName) {
|
||||
this.simpleName = simpleName;
|
||||
}
|
||||
|
||||
public int getIno() {
|
||||
return this.header.getIno();
|
||||
}
|
||||
|
||||
public int getNlink() {
|
||||
return this.nlink;
|
||||
}
|
||||
|
||||
public void setNlink(int nlink) {
|
||||
this.nlink = nlink;
|
||||
}
|
||||
|
||||
public Date getCreationTime() {
|
||||
return new Date(this.ctime);
|
||||
}
|
||||
|
||||
public void setCreationTime(Date ctime) {
|
||||
this.ctime = ctime.getTime();
|
||||
}
|
||||
|
||||
public int getGeneration() {
|
||||
return this.generation;
|
||||
}
|
||||
|
||||
public void setGeneration(int generation) {
|
||||
this.generation = generation;
|
||||
}
|
||||
|
||||
public boolean isDeleted() {
|
||||
return this.isDeleted;
|
||||
}
|
||||
|
||||
public void setDeleted(boolean isDeleted) {
|
||||
this.isDeleted = isDeleted;
|
||||
}
|
||||
|
||||
public long getOffset() {
|
||||
return this.offset;
|
||||
}
|
||||
|
||||
public void setOffset(long offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public int getVolume() {
|
||||
return this.volume;
|
||||
}
|
||||
|
||||
public void setVolume(int volume) {
|
||||
this.volume = volume;
|
||||
}
|
||||
|
||||
public DumpArchiveConstants.SEGMENT_TYPE getHeaderType() {
|
||||
return this.header.getType();
|
||||
}
|
||||
|
||||
public int getHeaderCount() {
|
||||
return this.header.getCount();
|
||||
}
|
||||
|
||||
public int getHeaderHoles() {
|
||||
return this.header.getHoles();
|
||||
}
|
||||
|
||||
public boolean isSparseRecord(int idx) {
|
||||
return ((this.header.getCdata(idx) & 0x1) == 0);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return this.ino;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == this)
|
||||
return true;
|
||||
if (o == null || !o.getClass().equals(getClass()))
|
||||
return false;
|
||||
DumpArchiveEntry rhs = (DumpArchiveEntry)o;
|
||||
if (this.header == null || rhs.header == null)
|
||||
return false;
|
||||
if (this.ino != rhs.ino)
|
||||
return false;
|
||||
if ((this.summary == null && rhs.summary != null) || (this.summary != null && !this.summary.equals(rhs.summary)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
static DumpArchiveEntry parse(byte[] buffer) {
|
||||
DumpArchiveEntry entry = new DumpArchiveEntry();
|
||||
TapeSegmentHeader header = entry.header;
|
||||
header.type = DumpArchiveConstants.SEGMENT_TYPE.find(DumpArchiveUtil.convert32(buffer, 0));
|
||||
header.volume = DumpArchiveUtil.convert32(buffer, 12);
|
||||
entry.ino = header.ino = DumpArchiveUtil.convert32(buffer, 20);
|
||||
int m = DumpArchiveUtil.convert16(buffer, 32);
|
||||
entry.setType(TYPE.find(m >> 12 & 0xF));
|
||||
entry.setMode(m);
|
||||
entry.nlink = DumpArchiveUtil.convert16(buffer, 34);
|
||||
entry.setSize(DumpArchiveUtil.convert64(buffer, 40));
|
||||
long t = 1000L * (long)DumpArchiveUtil.convert32(buffer, 48) + (long)(DumpArchiveUtil.convert32(buffer, 52) / 1000);
|
||||
entry.setAccessTime(new Date(t));
|
||||
t = 1000L * (long)DumpArchiveUtil.convert32(buffer, 56) + (long)(DumpArchiveUtil.convert32(buffer, 60) / 1000);
|
||||
entry.setLastModifiedDate(new Date(t));
|
||||
t = 1000L * (long)DumpArchiveUtil.convert32(buffer, 64) + (long)(DumpArchiveUtil.convert32(buffer, 68) / 1000);
|
||||
entry.ctime = t;
|
||||
entry.generation = DumpArchiveUtil.convert32(buffer, 140);
|
||||
entry.setUserId(DumpArchiveUtil.convert32(buffer, 144));
|
||||
entry.setGroupId(DumpArchiveUtil.convert32(buffer, 148));
|
||||
header.count = DumpArchiveUtil.convert32(buffer, 160);
|
||||
header.holes = 0;
|
||||
for (int i = 0; i < 512 && i < header.count; i++) {
|
||||
if (buffer[164 + i] == 0)
|
||||
header.holes++;
|
||||
}
|
||||
System.arraycopy(buffer, 164, header.cdata, 0, 512);
|
||||
entry.volume = header.getVolume();
|
||||
return entry;
|
||||
}
|
||||
|
||||
void update(byte[] buffer) {
|
||||
this.header.volume = DumpArchiveUtil.convert32(buffer, 16);
|
||||
this.header.count = DumpArchiveUtil.convert32(buffer, 160);
|
||||
this.header.holes = 0;
|
||||
for (int i = 0; i < 512 && i < this.header.count; i++) {
|
||||
if (buffer[164 + i] == 0)
|
||||
this.header.holes++;
|
||||
}
|
||||
System.arraycopy(buffer, 164, this.header.cdata, 0, 512);
|
||||
}
|
||||
|
||||
static class TapeSegmentHeader {
|
||||
private DumpArchiveConstants.SEGMENT_TYPE type;
|
||||
|
||||
private int volume;
|
||||
|
||||
private int ino;
|
||||
|
||||
private int count;
|
||||
|
||||
private int holes;
|
||||
|
||||
private final byte[] cdata = new byte[512];
|
||||
|
||||
public DumpArchiveConstants.SEGMENT_TYPE getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public int getVolume() {
|
||||
return this.volume;
|
||||
}
|
||||
|
||||
public int getIno() {
|
||||
return this.ino;
|
||||
}
|
||||
|
||||
void setIno(int ino) {
|
||||
this.ino = ino;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return this.count;
|
||||
}
|
||||
|
||||
public int getHoles() {
|
||||
return this.holes;
|
||||
}
|
||||
|
||||
public int getCdata(int idx) {
|
||||
return this.cdata[idx];
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
String getOriginalName() {
|
||||
return this.originalName;
|
||||
}
|
||||
|
||||
public final void setName(String name) {
|
||||
this.originalName = name;
|
||||
if (name != null) {
|
||||
if (isDirectory() && !name.endsWith("/"))
|
||||
name = name + "/";
|
||||
if (name.startsWith("./"))
|
||||
name = name.substring(2);
|
||||
}
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Date getLastModifiedDate() {
|
||||
return new Date(this.mtime);
|
||||
}
|
||||
|
||||
public boolean isDirectory() {
|
||||
return (this.type == TYPE.DIRECTORY);
|
||||
}
|
||||
|
||||
public boolean isFile() {
|
||||
return (this.type == TYPE.FILE);
|
||||
}
|
||||
|
||||
public boolean isSocket() {
|
||||
return (this.type == TYPE.SOCKET);
|
||||
}
|
||||
|
||||
public boolean isChrDev() {
|
||||
return (this.type == TYPE.CHRDEV);
|
||||
}
|
||||
|
||||
public boolean isBlkDev() {
|
||||
return (this.type == TYPE.BLKDEV);
|
||||
}
|
||||
|
||||
public boolean isFifo() {
|
||||
return (this.type == TYPE.FIFO);
|
||||
}
|
||||
|
||||
public TYPE getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public void setType(TYPE type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public int getMode() {
|
||||
return this.mode;
|
||||
}
|
||||
|
||||
public void setMode(int mode) {
|
||||
this.mode = mode & 0xFFF;
|
||||
this.permissions = PERMISSION.find(mode);
|
||||
}
|
||||
|
||||
public Set<PERMISSION> getPermissions() {
|
||||
return this.permissions;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return isDirectory() ? -1L : this.size;
|
||||
}
|
||||
|
||||
long getEntrySize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public void setSize(long size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public void setLastModifiedDate(Date mtime) {
|
||||
this.mtime = mtime.getTime();
|
||||
}
|
||||
|
||||
public Date getAccessTime() {
|
||||
return new Date(this.atime);
|
||||
}
|
||||
|
||||
public void setAccessTime(Date atime) {
|
||||
this.atime = atime.getTime();
|
||||
}
|
||||
|
||||
public int getUserId() {
|
||||
return this.uid;
|
||||
}
|
||||
|
||||
public void setUserId(int uid) {
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
public int getGroupId() {
|
||||
return this.gid;
|
||||
}
|
||||
|
||||
public void setGroupId(int gid) {
|
||||
this.gid = gid;
|
||||
}
|
||||
|
||||
public enum TYPE {
|
||||
WHITEOUT(14),
|
||||
SOCKET(12),
|
||||
LINK(10),
|
||||
FILE(8),
|
||||
BLKDEV(6),
|
||||
DIRECTORY(4),
|
||||
CHRDEV(2),
|
||||
FIFO(1),
|
||||
UNKNOWN(15);
|
||||
|
||||
private int code;
|
||||
|
||||
TYPE(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public static TYPE find(int code) {
|
||||
TYPE type = UNKNOWN;
|
||||
for (TYPE t : values()) {
|
||||
if (code == t.code)
|
||||
type = t;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
public enum PERMISSION {
|
||||
SETUID(2048),
|
||||
SETGUI(1024),
|
||||
STICKY(512),
|
||||
USER_READ(256),
|
||||
USER_WRITE(128),
|
||||
USER_EXEC(64),
|
||||
GROUP_READ(32),
|
||||
GROUP_WRITE(16),
|
||||
GROUP_EXEC(8),
|
||||
WORLD_READ(4),
|
||||
WORLD_WRITE(2),
|
||||
WORLD_EXEC(1);
|
||||
|
||||
private int code;
|
||||
|
||||
PERMISSION(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public static Set<PERMISSION> find(int code) {
|
||||
Set<PERMISSION> set = new HashSet<PERMISSION>();
|
||||
for (PERMISSION p : values()) {
|
||||
if ((code & p.code) == p.code)
|
||||
set.add(p);
|
||||
}
|
||||
if (set.isEmpty())
|
||||
return Collections.<PERMISSION>emptySet();
|
||||
return EnumSet.<PERMISSION>copyOf((Collection)set);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package org.apache.commons.compress.archivers.dump;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DumpArchiveException extends IOException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public DumpArchiveException() {}
|
||||
|
||||
public DumpArchiveException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public DumpArchiveException(Throwable cause) {
|
||||
initCause(cause);
|
||||
}
|
||||
|
||||
public DumpArchiveException(String msg, Throwable cause) {
|
||||
super(msg);
|
||||
initCause(cause);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,299 @@
|
|||
package org.apache.commons.compress.archivers.dump;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.Queue;
|
||||
import java.util.Stack;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.ArchiveException;
|
||||
import org.apache.commons.compress.archivers.ArchiveInputStream;
|
||||
import org.apache.commons.compress.archivers.zip.ZipEncoding;
|
||||
import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
|
||||
|
||||
public class DumpArchiveInputStream extends ArchiveInputStream {
|
||||
private DumpArchiveSummary summary;
|
||||
|
||||
private DumpArchiveEntry active;
|
||||
|
||||
private boolean isClosed;
|
||||
|
||||
private boolean hasHitEOF;
|
||||
|
||||
private long entrySize;
|
||||
|
||||
private long entryOffset;
|
||||
|
||||
private int readIdx;
|
||||
|
||||
private final byte[] readBuf = new byte[1024];
|
||||
|
||||
private byte[] blockBuffer;
|
||||
|
||||
private int recordOffset;
|
||||
|
||||
private long filepos;
|
||||
|
||||
protected TapeInputStream raw;
|
||||
|
||||
private final Map<Integer, Dirent> names = new HashMap<Integer, Dirent>();
|
||||
|
||||
private final Map<Integer, DumpArchiveEntry> pending = new HashMap<Integer, DumpArchiveEntry>();
|
||||
|
||||
private Queue<DumpArchiveEntry> queue;
|
||||
|
||||
private final ZipEncoding encoding;
|
||||
|
||||
public DumpArchiveInputStream(InputStream is) throws ArchiveException {
|
||||
this(is, null);
|
||||
}
|
||||
|
||||
public DumpArchiveInputStream(InputStream is, String encoding) throws ArchiveException {
|
||||
this.raw = new TapeInputStream(is);
|
||||
this.hasHitEOF = false;
|
||||
this.encoding = ZipEncodingHelper.getZipEncoding(encoding);
|
||||
try {
|
||||
byte[] headerBytes = this.raw.readRecord();
|
||||
if (!DumpArchiveUtil.verify(headerBytes))
|
||||
throw new UnrecognizedFormatException();
|
||||
this.summary = new DumpArchiveSummary(headerBytes, this.encoding);
|
||||
this.raw.resetBlockSize(this.summary.getNTRec(), this.summary.isCompressed());
|
||||
this.blockBuffer = new byte[4096];
|
||||
readCLRI();
|
||||
readBITS();
|
||||
} catch (IOException ex) {
|
||||
throw new ArchiveException(ex.getMessage(), ex);
|
||||
}
|
||||
Dirent root = new Dirent(2, 2, 4, ".");
|
||||
this.names.put(Integer.valueOf(2), root);
|
||||
this.queue = new PriorityQueue<DumpArchiveEntry>(10, new Comparator<DumpArchiveEntry>() {
|
||||
public int compare(DumpArchiveEntry p, DumpArchiveEntry q) {
|
||||
if (p.getOriginalName() == null || q.getOriginalName() == null)
|
||||
return Integer.MAX_VALUE;
|
||||
return p.getOriginalName().compareTo(q.getOriginalName());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getCount() {
|
||||
return (int)getBytesRead();
|
||||
}
|
||||
|
||||
public long getBytesRead() {
|
||||
return this.raw.getBytesRead();
|
||||
}
|
||||
|
||||
public DumpArchiveSummary getSummary() {
|
||||
return this.summary;
|
||||
}
|
||||
|
||||
private void readCLRI() throws IOException {
|
||||
byte[] buffer = this.raw.readRecord();
|
||||
if (!DumpArchiveUtil.verify(buffer))
|
||||
throw new InvalidFormatException();
|
||||
this.active = DumpArchiveEntry.parse(buffer);
|
||||
if (DumpArchiveConstants.SEGMENT_TYPE.CLRI != this.active.getHeaderType())
|
||||
throw new InvalidFormatException();
|
||||
if (this.raw.skip((long)(1024 * this.active.getHeaderCount())) == -1L)
|
||||
throw new EOFException();
|
||||
this.readIdx = this.active.getHeaderCount();
|
||||
}
|
||||
|
||||
private void readBITS() throws IOException {
|
||||
byte[] buffer = this.raw.readRecord();
|
||||
if (!DumpArchiveUtil.verify(buffer))
|
||||
throw new InvalidFormatException();
|
||||
this.active = DumpArchiveEntry.parse(buffer);
|
||||
if (DumpArchiveConstants.SEGMENT_TYPE.BITS != this.active.getHeaderType())
|
||||
throw new InvalidFormatException();
|
||||
if (this.raw.skip((long)(1024 * this.active.getHeaderCount())) == -1L)
|
||||
throw new EOFException();
|
||||
this.readIdx = this.active.getHeaderCount();
|
||||
}
|
||||
|
||||
public DumpArchiveEntry getNextDumpEntry() throws IOException {
|
||||
return getNextEntry();
|
||||
}
|
||||
|
||||
public DumpArchiveEntry getNextEntry() throws IOException {
|
||||
DumpArchiveEntry entry = null;
|
||||
String path = null;
|
||||
if (!this.queue.isEmpty())
|
||||
return this.queue.remove();
|
||||
while (entry == null) {
|
||||
if (this.hasHitEOF)
|
||||
return null;
|
||||
while (this.readIdx < this.active.getHeaderCount()) {
|
||||
if (!this.active.isSparseRecord(this.readIdx++) && this.raw.skip(1024L) == -1L)
|
||||
throw new EOFException();
|
||||
}
|
||||
this.readIdx = 0;
|
||||
this.filepos = this.raw.getBytesRead();
|
||||
byte[] headerBytes = this.raw.readRecord();
|
||||
if (!DumpArchiveUtil.verify(headerBytes))
|
||||
throw new InvalidFormatException();
|
||||
this.active = DumpArchiveEntry.parse(headerBytes);
|
||||
while (DumpArchiveConstants.SEGMENT_TYPE.ADDR == this.active.getHeaderType()) {
|
||||
if (this.raw.skip((long)(1024 * (this.active.getHeaderCount() - this.active.getHeaderHoles()))) == -1L)
|
||||
throw new EOFException();
|
||||
this.filepos = this.raw.getBytesRead();
|
||||
headerBytes = this.raw.readRecord();
|
||||
if (!DumpArchiveUtil.verify(headerBytes))
|
||||
throw new InvalidFormatException();
|
||||
this.active = DumpArchiveEntry.parse(headerBytes);
|
||||
}
|
||||
if (DumpArchiveConstants.SEGMENT_TYPE.END == this.active.getHeaderType()) {
|
||||
this.hasHitEOF = true;
|
||||
return null;
|
||||
}
|
||||
entry = this.active;
|
||||
if (entry.isDirectory()) {
|
||||
readDirectoryEntry(this.active);
|
||||
this.entryOffset = 0L;
|
||||
this.entrySize = 0L;
|
||||
this.readIdx = this.active.getHeaderCount();
|
||||
} else {
|
||||
this.entryOffset = 0L;
|
||||
this.entrySize = this.active.getEntrySize();
|
||||
this.readIdx = 0;
|
||||
}
|
||||
this.recordOffset = this.readBuf.length;
|
||||
path = getPath(entry);
|
||||
if (path == null)
|
||||
entry = null;
|
||||
}
|
||||
entry.setName(path);
|
||||
entry.setSimpleName(this.names.get(Integer.valueOf(entry.getIno())).getName());
|
||||
entry.setOffset(this.filepos);
|
||||
return entry;
|
||||
}
|
||||
|
||||
private void readDirectoryEntry(DumpArchiveEntry entry) throws IOException {
|
||||
long size = entry.getEntrySize();
|
||||
boolean first = true;
|
||||
while (first || DumpArchiveConstants.SEGMENT_TYPE.ADDR == entry.getHeaderType()) {
|
||||
if (!first)
|
||||
this.raw.readRecord();
|
||||
if (!this.names.containsKey(Integer.valueOf(entry.getIno())) && DumpArchiveConstants.SEGMENT_TYPE.INODE == entry.getHeaderType())
|
||||
this.pending.put(Integer.valueOf(entry.getIno()), entry);
|
||||
int datalen = 1024 * entry.getHeaderCount();
|
||||
if (this.blockBuffer.length < datalen)
|
||||
this.blockBuffer = new byte[datalen];
|
||||
if (this.raw.read(this.blockBuffer, 0, datalen) != datalen)
|
||||
throw new EOFException();
|
||||
int reclen = 0;
|
||||
for (int i = 0; i < datalen - 8 && (long)i < size - 8L;
|
||||
i += reclen) {
|
||||
int ino = DumpArchiveUtil.convert32(this.blockBuffer, i);
|
||||
reclen = DumpArchiveUtil.convert16(this.blockBuffer, i + 4);
|
||||
byte type = this.blockBuffer[i + 6];
|
||||
String name = DumpArchiveUtil.decode(this.encoding, this.blockBuffer, i + 8, this.blockBuffer[i + 7]);
|
||||
if (!".".equals(name) && !"..".equals(name)) {
|
||||
Dirent d = new Dirent(ino, entry.getIno(), type, name);
|
||||
this.names.put(Integer.valueOf(ino), d);
|
||||
for (Map.Entry<Integer, DumpArchiveEntry> e : this.pending.entrySet()) {
|
||||
String path = getPath(e.getValue());
|
||||
if (path != null) {
|
||||
e.getValue().setName(path);
|
||||
e.getValue().setSimpleName(this.names.get(e.getKey()).getName());
|
||||
this.queue.add(e.getValue());
|
||||
}
|
||||
}
|
||||
for (DumpArchiveEntry e : this.queue)
|
||||
this.pending.remove(Integer.valueOf(e.getIno()));
|
||||
}
|
||||
}
|
||||
byte[] peekBytes = this.raw.peek();
|
||||
if (!DumpArchiveUtil.verify(peekBytes))
|
||||
throw new InvalidFormatException();
|
||||
entry = DumpArchiveEntry.parse(peekBytes);
|
||||
first = false;
|
||||
size -= 1024L;
|
||||
}
|
||||
}
|
||||
|
||||
private String getPath(DumpArchiveEntry entry) {
|
||||
Stack<String> elements = new Stack<String>();
|
||||
Dirent dirent = null;
|
||||
for (int i = entry.getIno();; i = dirent.getParentIno()) {
|
||||
if (!this.names.containsKey(Integer.valueOf(i))) {
|
||||
elements.clear();
|
||||
break;
|
||||
}
|
||||
dirent = this.names.get(Integer.valueOf(i));
|
||||
elements.push(dirent.getName());
|
||||
if (dirent.getIno() == dirent.getParentIno())
|
||||
break;
|
||||
}
|
||||
if (elements.isEmpty()) {
|
||||
this.pending.put(Integer.valueOf(entry.getIno()), entry);
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(elements.pop());
|
||||
while (!elements.isEmpty()) {
|
||||
sb.append('/');
|
||||
sb.append(elements.pop());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public int read(byte[] buf, int off, int len) throws IOException {
|
||||
int totalRead = 0;
|
||||
if (this.hasHitEOF || this.isClosed || this.entryOffset >= this.entrySize)
|
||||
return -1;
|
||||
if (this.active == null)
|
||||
throw new IllegalStateException("No current dump entry");
|
||||
if ((long)len + this.entryOffset > this.entrySize)
|
||||
len = (int)(this.entrySize - this.entryOffset);
|
||||
while (len > 0) {
|
||||
int sz = (len > this.readBuf.length - this.recordOffset) ? (this.readBuf.length - this.recordOffset) : len;
|
||||
if (this.recordOffset + sz <= this.readBuf.length) {
|
||||
System.arraycopy(this.readBuf, this.recordOffset, buf, off, sz);
|
||||
totalRead += sz;
|
||||
this.recordOffset += sz;
|
||||
len -= sz;
|
||||
off += sz;
|
||||
}
|
||||
if (len > 0) {
|
||||
if (this.readIdx >= 512) {
|
||||
byte[] headerBytes = this.raw.readRecord();
|
||||
if (!DumpArchiveUtil.verify(headerBytes))
|
||||
throw new InvalidFormatException();
|
||||
this.active = DumpArchiveEntry.parse(headerBytes);
|
||||
this.readIdx = 0;
|
||||
}
|
||||
if (!this.active.isSparseRecord(this.readIdx++)) {
|
||||
int r = this.raw.read(this.readBuf, 0, this.readBuf.length);
|
||||
if (r != this.readBuf.length)
|
||||
throw new EOFException();
|
||||
} else {
|
||||
Arrays.fill(this.readBuf, (byte)0);
|
||||
}
|
||||
this.recordOffset = 0;
|
||||
}
|
||||
}
|
||||
this.entryOffset += (long)totalRead;
|
||||
return totalRead;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (!this.isClosed) {
|
||||
this.isClosed = true;
|
||||
this.raw.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean matches(byte[] buffer, int length) {
|
||||
if (length < 32)
|
||||
return false;
|
||||
if (length >= 1024)
|
||||
return DumpArchiveUtil.verify(buffer);
|
||||
return (60012 == DumpArchiveUtil.convert32(buffer, 24));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
package org.apache.commons.compress.archivers.dump;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import org.apache.commons.compress.archivers.zip.ZipEncoding;
|
||||
|
||||
public class DumpArchiveSummary {
|
||||
private long dumpDate;
|
||||
|
||||
private long previousDumpDate;
|
||||
|
||||
private int volume;
|
||||
|
||||
private String label;
|
||||
|
||||
private int level;
|
||||
|
||||
private String filesys;
|
||||
|
||||
private String devname;
|
||||
|
||||
private String hostname;
|
||||
|
||||
private int flags;
|
||||
|
||||
private int firstrec;
|
||||
|
||||
private int ntrec;
|
||||
|
||||
DumpArchiveSummary(byte[] buffer, ZipEncoding encoding) throws IOException {
|
||||
this.dumpDate = 1000L * (long)DumpArchiveUtil.convert32(buffer, 4);
|
||||
this.previousDumpDate = 1000L * (long)DumpArchiveUtil.convert32(buffer, 8);
|
||||
this.volume = DumpArchiveUtil.convert32(buffer, 12);
|
||||
this.label = DumpArchiveUtil.decode(encoding, buffer, 676, 16).trim();
|
||||
this.level = DumpArchiveUtil.convert32(buffer, 692);
|
||||
this.filesys = DumpArchiveUtil.decode(encoding, buffer, 696, 64).trim();
|
||||
this.devname = DumpArchiveUtil.decode(encoding, buffer, 760, 64).trim();
|
||||
this.hostname = DumpArchiveUtil.decode(encoding, buffer, 824, 64).trim();
|
||||
this.flags = DumpArchiveUtil.convert32(buffer, 888);
|
||||
this.firstrec = DumpArchiveUtil.convert32(buffer, 892);
|
||||
this.ntrec = DumpArchiveUtil.convert32(buffer, 896);
|
||||
}
|
||||
|
||||
public Date getDumpDate() {
|
||||
return new Date(this.dumpDate);
|
||||
}
|
||||
|
||||
public void setDumpDate(Date dumpDate) {
|
||||
this.dumpDate = dumpDate.getTime();
|
||||
}
|
||||
|
||||
public Date getPreviousDumpDate() {
|
||||
return new Date(this.previousDumpDate);
|
||||
}
|
||||
|
||||
public void setPreviousDumpDate(Date previousDumpDate) {
|
||||
this.previousDumpDate = previousDumpDate.getTime();
|
||||
}
|
||||
|
||||
public int getVolume() {
|
||||
return this.volume;
|
||||
}
|
||||
|
||||
public void setVolume(int volume) {
|
||||
this.volume = volume;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return this.level;
|
||||
}
|
||||
|
||||
public void setLevel(int level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getFilesystem() {
|
||||
return this.filesys;
|
||||
}
|
||||
|
||||
public void setFilesystem(String filesystem) {
|
||||
this.filesys = filesystem;
|
||||
}
|
||||
|
||||
public String getDevname() {
|
||||
return this.devname;
|
||||
}
|
||||
|
||||
public void setDevname(String devname) {
|
||||
this.devname = devname;
|
||||
}
|
||||
|
||||
public String getHostname() {
|
||||
return this.hostname;
|
||||
}
|
||||
|
||||
public void setHostname(String hostname) {
|
||||
this.hostname = hostname;
|
||||
}
|
||||
|
||||
public int getFlags() {
|
||||
return this.flags;
|
||||
}
|
||||
|
||||
public void setFlags(int flags) {
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
public int getFirstRecord() {
|
||||
return this.firstrec;
|
||||
}
|
||||
|
||||
public void setFirstRecord(int firstrec) {
|
||||
this.firstrec = firstrec;
|
||||
}
|
||||
|
||||
public int getNTRec() {
|
||||
return this.ntrec;
|
||||
}
|
||||
|
||||
public void setNTRec(int ntrec) {
|
||||
this.ntrec = ntrec;
|
||||
}
|
||||
|
||||
public boolean isNewHeader() {
|
||||
return ((this.flags & 0x1) == 1);
|
||||
}
|
||||
|
||||
public boolean isNewInode() {
|
||||
return ((this.flags & 0x2) == 2);
|
||||
}
|
||||
|
||||
public boolean isCompressed() {
|
||||
return ((this.flags & 0x80) == 128);
|
||||
}
|
||||
|
||||
public boolean isMetaDataOnly() {
|
||||
return ((this.flags & 0x100) == 256);
|
||||
}
|
||||
|
||||
public boolean isExtendedAttributes() {
|
||||
return ((this.flags & 0x8000) == 32768);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hash = 17;
|
||||
if (this.label != null)
|
||||
hash = this.label.hashCode();
|
||||
hash = (int)((long)hash + 31L * this.dumpDate);
|
||||
if (this.hostname != null)
|
||||
hash = 31 * this.hostname.hashCode() + 17;
|
||||
if (this.devname != null)
|
||||
hash = 31 * this.devname.hashCode() + 17;
|
||||
return hash;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || !o.getClass().equals(getClass()))
|
||||
return false;
|
||||
DumpArchiveSummary rhs = (DumpArchiveSummary)o;
|
||||
if (this.dumpDate != rhs.dumpDate)
|
||||
return false;
|
||||
if (getHostname() == null || !getHostname().equals(rhs.getHostname()))
|
||||
return false;
|
||||
if (getDevname() == null || !getDevname().equals(rhs.getDevname()))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package org.apache.commons.compress.archivers.dump;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.apache.commons.compress.archivers.zip.ZipEncoding;
|
||||
|
||||
class DumpArchiveUtil {
|
||||
public static int calculateChecksum(byte[] buffer) {
|
||||
int calc = 0;
|
||||
for (int i = 0; i < 256; i++)
|
||||
calc += convert32(buffer, 4 * i);
|
||||
return 84446 - (calc - convert32(buffer, 28));
|
||||
}
|
||||
|
||||
public static final boolean verify(byte[] buffer) {
|
||||
int magic = convert32(buffer, 24);
|
||||
if (magic != 60012)
|
||||
return false;
|
||||
int checksum = convert32(buffer, 28);
|
||||
if (checksum != calculateChecksum(buffer))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static final int getIno(byte[] buffer) {
|
||||
return convert32(buffer, 20);
|
||||
}
|
||||
|
||||
public static final long convert64(byte[] buffer, int offset) {
|
||||
long i = 0L;
|
||||
i += (long)buffer[offset + 7] << 56L;
|
||||
i += (long)buffer[offset + 6] << 48L & 0xFF000000000000L;
|
||||
i += (long)buffer[offset + 5] << 40L & 0xFF0000000000L;
|
||||
i += (long)buffer[offset + 4] << 32L & 0xFF00000000L;
|
||||
i += (long)buffer[offset + 3] << 24L & 0xFF000000L;
|
||||
i += (long)buffer[offset + 2] << 16L & 0xFF0000L;
|
||||
i += (long)buffer[offset + 1] << 8L & 0xFF00L;
|
||||
i += (long)buffer[offset] & 0xFFL;
|
||||
return i;
|
||||
}
|
||||
|
||||
public static final int convert32(byte[] buffer, int offset) {
|
||||
int i = 0;
|
||||
i = buffer[offset + 3] << 24;
|
||||
i += buffer[offset + 2] << 16 & 0xFF0000;
|
||||
i += buffer[offset + 1] << 8 & 0xFF00;
|
||||
i += buffer[offset] & 0xFF;
|
||||
return i;
|
||||
}
|
||||
|
||||
public static final int convert16(byte[] buffer, int offset) {
|
||||
int i = 0;
|
||||
i += buffer[offset + 1] << 8 & 0xFF00;
|
||||
i += buffer[offset] & 0xFF;
|
||||
return i;
|
||||
}
|
||||
|
||||
static String decode(ZipEncoding encoding, byte[] b, int offset, int len) throws IOException {
|
||||
byte[] copy = new byte[len];
|
||||
System.arraycopy(b, offset, copy, 0, len);
|
||||
return encoding.decode(copy);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package org.apache.commons.compress.archivers.dump;
|
||||
|
||||
public class InvalidFormatException extends DumpArchiveException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected long offset;
|
||||
|
||||
public InvalidFormatException() {
|
||||
super("there was an error decoding a tape segment");
|
||||
}
|
||||
|
||||
public InvalidFormatException(long offset) {
|
||||
super("there was an error decoding a tape segment header at offset " + offset + ".");
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public long getOffset() {
|
||||
return this.offset;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package org.apache.commons.compress.archivers.dump;
|
||||
|
||||
public class ShortFileException extends DumpArchiveException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ShortFileException() {
|
||||
super("unexpected EOF");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
package org.apache.commons.compress.archivers.dump;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Inflater;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
|
||||
class TapeInputStream extends FilterInputStream {
|
||||
private byte[] blockBuffer = new byte[1024];
|
||||
|
||||
private int currBlkIdx = -1;
|
||||
|
||||
private int blockSize = 1024;
|
||||
|
||||
private static final int recordSize = 1024;
|
||||
|
||||
private int readOffset = 1024;
|
||||
|
||||
private boolean isCompressed = false;
|
||||
|
||||
private long bytesRead = 0L;
|
||||
|
||||
public TapeInputStream(InputStream in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
public void resetBlockSize(int recsPerBlock, boolean isCompressed) throws IOException {
|
||||
this.isCompressed = isCompressed;
|
||||
this.blockSize = 1024 * recsPerBlock;
|
||||
byte[] oldBuffer = this.blockBuffer;
|
||||
this.blockBuffer = new byte[this.blockSize];
|
||||
System.arraycopy(oldBuffer, 0, this.blockBuffer, 0, 1024);
|
||||
readFully(this.blockBuffer, 1024, this.blockSize - 1024);
|
||||
this.currBlkIdx = 0;
|
||||
this.readOffset = 1024;
|
||||
}
|
||||
|
||||
public int available() throws IOException {
|
||||
if (this.readOffset < this.blockSize)
|
||||
return this.blockSize - this.readOffset;
|
||||
return this.in.available();
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
throw new IllegalArgumentException("all reads must be multiple of record size (1024 bytes.");
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
if (len % 1024 != 0)
|
||||
throw new IllegalArgumentException("all reads must be multiple of record size (1024 bytes.");
|
||||
int bytes = 0;
|
||||
while (bytes < len) {
|
||||
if (this.readOffset == this.blockSize && !readBlock(true))
|
||||
return -1;
|
||||
int n = 0;
|
||||
if (this.readOffset + len - bytes <= this.blockSize) {
|
||||
n = len - bytes;
|
||||
} else {
|
||||
n = this.blockSize - this.readOffset;
|
||||
}
|
||||
System.arraycopy(this.blockBuffer, this.readOffset, b, off, n);
|
||||
this.readOffset += n;
|
||||
bytes += n;
|
||||
off += n;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public long skip(long len) throws IOException {
|
||||
if (len % 1024L != 0L)
|
||||
throw new IllegalArgumentException("all reads must be multiple of record size (1024 bytes.");
|
||||
long bytes = 0L;
|
||||
while (bytes < len) {
|
||||
if (this.readOffset == this.blockSize)
|
||||
if (!readBlock((len - bytes < (long)this.blockSize)))
|
||||
return -1L;
|
||||
long n = 0L;
|
||||
if ((long)this.readOffset + len - bytes <= (long)this.blockSize) {
|
||||
n = len - bytes;
|
||||
} else {
|
||||
n = (long)(this.blockSize - this.readOffset);
|
||||
}
|
||||
this.readOffset = (int)((long)this.readOffset + n);
|
||||
bytes += n;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (this.in != null && this.in != System.in)
|
||||
this.in.close();
|
||||
}
|
||||
|
||||
public byte[] peek() throws IOException {
|
||||
if (this.readOffset == this.blockSize && !readBlock(true))
|
||||
return null;
|
||||
byte[] b = new byte[1024];
|
||||
System.arraycopy(this.blockBuffer, this.readOffset, b, 0, b.length);
|
||||
return b;
|
||||
}
|
||||
|
||||
public byte[] readRecord() throws IOException {
|
||||
byte[] result = new byte[1024];
|
||||
if (-1 == read(result, 0, result.length))
|
||||
throw new ShortFileException();
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean readBlock(boolean decompress) throws IOException {
|
||||
boolean success = true;
|
||||
if (this.in == null)
|
||||
throw new IOException("input buffer is closed");
|
||||
if (!this.isCompressed || this.currBlkIdx == -1) {
|
||||
success = readFully(this.blockBuffer, 0, this.blockSize);
|
||||
this.bytesRead += (long)this.blockSize;
|
||||
} else {
|
||||
if (!readFully(this.blockBuffer, 0, 4))
|
||||
return false;
|
||||
this.bytesRead += 4L;
|
||||
int h = DumpArchiveUtil.convert32(this.blockBuffer, 0);
|
||||
boolean compressed = ((h & 0x1) == 1);
|
||||
if (!compressed) {
|
||||
success = readFully(this.blockBuffer, 0, this.blockSize);
|
||||
this.bytesRead += (long)this.blockSize;
|
||||
} else {
|
||||
int flags = h >> 1 & 0x7;
|
||||
int length = h >> 4 & 0xFFFFFFF;
|
||||
byte[] compBuffer = new byte[length];
|
||||
success = readFully(compBuffer, 0, length);
|
||||
this.bytesRead += (long)length;
|
||||
if (!decompress) {
|
||||
Arrays.fill(this.blockBuffer, (byte)0);
|
||||
} else {
|
||||
switch (DumpArchiveConstants.COMPRESSION_TYPE.find(flags & 0x3)) {
|
||||
case ZLIB:
|
||||
try {
|
||||
Inflater inflator = new Inflater();
|
||||
inflator.setInput(compBuffer, 0, compBuffer.length);
|
||||
length = inflator.inflate(this.blockBuffer);
|
||||
if (length != this.blockSize)
|
||||
throw new ShortFileException();
|
||||
inflator.end();
|
||||
} catch (DataFormatException e) {
|
||||
throw new DumpArchiveException("bad data", e);
|
||||
}
|
||||
break;
|
||||
case BZLIB:
|
||||
throw new UnsupportedCompressionAlgorithmException("BZLIB2");
|
||||
case LZO:
|
||||
throw new UnsupportedCompressionAlgorithmException("LZO");
|
||||
default:
|
||||
throw new UnsupportedCompressionAlgorithmException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.currBlkIdx++;
|
||||
this.readOffset = 0;
|
||||
return success;
|
||||
}
|
||||
|
||||
private boolean readFully(byte[] b, int off, int len) throws IOException {
|
||||
int count = IOUtils.readFully(this.in, b, off, len);
|
||||
if (count < len)
|
||||
throw new ShortFileException();
|
||||
return true;
|
||||
}
|
||||
|
||||
public long getBytesRead() {
|
||||
return this.bytesRead;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package org.apache.commons.compress.archivers.dump;
|
||||
|
||||
public class UnrecognizedFormatException extends DumpArchiveException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public UnrecognizedFormatException() {
|
||||
super("this is not a recognized format.");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package org.apache.commons.compress.archivers.dump;
|
||||
|
||||
public class UnsupportedCompressionAlgorithmException extends DumpArchiveException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public UnsupportedCompressionAlgorithmException() {
|
||||
super("this file uses an unsupported compression algorithm.");
|
||||
}
|
||||
|
||||
public UnsupportedCompressionAlgorithmException(String alg) {
|
||||
super("this file uses an unsupported compression algorithm: " + alg + ".");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package org.apache.commons.compress.archivers.jar;
|
||||
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipException;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
|
||||
public class JarArchiveEntry extends ZipArchiveEntry {
|
||||
private final Attributes manifestAttributes = null;
|
||||
|
||||
private final Certificate[] certificates = null;
|
||||
|
||||
public JarArchiveEntry(ZipEntry entry) throws ZipException {
|
||||
super(entry);
|
||||
}
|
||||
|
||||
public JarArchiveEntry(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
public JarArchiveEntry(ZipArchiveEntry entry) throws ZipException {
|
||||
super(entry);
|
||||
}
|
||||
|
||||
public JarArchiveEntry(JarEntry entry) throws ZipException {
|
||||
super(entry);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Attributes getManifestAttributes() {
|
||||
return this.manifestAttributes;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Certificate[] getCertificates() {
|
||||
if (this.certificates != null) {
|
||||
Certificate[] certs = new Certificate[this.certificates.length];
|
||||
System.arraycopy(this.certificates, 0, certs, 0, certs.length);
|
||||
return certs;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package org.apache.commons.compress.archivers.jar;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
|
||||
|
||||
public class JarArchiveInputStream extends ZipArchiveInputStream {
|
||||
public JarArchiveInputStream(InputStream inputStream) {
|
||||
super(inputStream);
|
||||
}
|
||||
|
||||
public JarArchiveEntry getNextJarEntry() throws IOException {
|
||||
ZipArchiveEntry entry = getNextZipEntry();
|
||||
return (entry == null) ? null : new JarArchiveEntry(entry);
|
||||
}
|
||||
|
||||
public ArchiveEntry getNextEntry() throws IOException {
|
||||
return getNextJarEntry();
|
||||
}
|
||||
|
||||
public static boolean matches(byte[] signature, int length) {
|
||||
return ZipArchiveInputStream.matches(signature, length);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package org.apache.commons.compress.archivers.jar;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.JarMarker;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
|
||||
|
||||
public class JarArchiveOutputStream extends ZipArchiveOutputStream {
|
||||
private boolean jarMarkerAdded = false;
|
||||
|
||||
public JarArchiveOutputStream(OutputStream out) {
|
||||
super(out);
|
||||
}
|
||||
|
||||
public void putArchiveEntry(ArchiveEntry ze) throws IOException {
|
||||
if (!this.jarMarkerAdded) {
|
||||
((ZipArchiveEntry)ze).addAsFirstExtraField(JarMarker.getInstance());
|
||||
this.jarMarkerAdded = true;
|
||||
}
|
||||
super.putArchiveEntry(ze);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
package org.apache.commons.compress.archivers.sevenz;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.CipherInputStream;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
class AES256SHA256Decoder extends CoderBase {
|
||||
AES256SHA256Decoder() {
|
||||
super(new Class<?>[0]);
|
||||
}
|
||||
|
||||
InputStream decode(final InputStream in, final Coder coder, final byte[] passwordBytes) throws IOException {
|
||||
return new InputStream() {
|
||||
private boolean isInitialized = false;
|
||||
|
||||
private CipherInputStream cipherInputStream = null;
|
||||
|
||||
private CipherInputStream init() throws IOException {
|
||||
byte[] aesKeyBytes;
|
||||
if (this.isInitialized)
|
||||
return this.cipherInputStream;
|
||||
int byte0 = 0xFF & coder.properties[0];
|
||||
int numCyclesPower = byte0 & 0x3F;
|
||||
int byte1 = 0xFF & coder.properties[1];
|
||||
int ivSize = (byte0 >> 6 & 0x1) + (byte1 & 0xF);
|
||||
int saltSize = (byte0 >> 7 & 0x1) + (byte1 >> 4);
|
||||
if (2 + saltSize + ivSize > coder.properties.length)
|
||||
throw new IOException("Salt size + IV size too long");
|
||||
byte[] salt = new byte[saltSize];
|
||||
System.arraycopy(coder.properties, 2, salt, 0, saltSize);
|
||||
byte[] iv = new byte[16];
|
||||
System.arraycopy(coder.properties, 2 + saltSize, iv, 0, ivSize);
|
||||
if (passwordBytes == null)
|
||||
throw new IOException("Cannot read encrypted files without a password");
|
||||
if (numCyclesPower == 63) {
|
||||
aesKeyBytes = new byte[32];
|
||||
System.arraycopy(salt, 0, aesKeyBytes, 0, saltSize);
|
||||
System.arraycopy(passwordBytes, 0, aesKeyBytes, saltSize, Math.min(passwordBytes.length, aesKeyBytes.length - saltSize));
|
||||
} else {
|
||||
MessageDigest digest;
|
||||
try {
|
||||
digest = MessageDigest.getInstance("SHA-256");
|
||||
} catch (NoSuchAlgorithmException noSuchAlgorithmException) {
|
||||
IOException ioe = new IOException("SHA-256 is unsupported by your Java implementation");
|
||||
ioe.initCause(noSuchAlgorithmException);
|
||||
throw ioe;
|
||||
}
|
||||
byte[] extra = new byte[8];
|
||||
for (long j = 0L; j < 1L << numCyclesPower; j++) {
|
||||
digest.update(salt);
|
||||
digest.update(passwordBytes);
|
||||
digest.update(extra);
|
||||
for (int k = 0; k < extra.length; k++) {
|
||||
extra[k] = (byte)(extra[k] + 1);
|
||||
if (extra[k] != 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
aesKeyBytes = digest.digest();
|
||||
}
|
||||
SecretKey aesKey = new SecretKeySpec(aesKeyBytes, "AES");
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
|
||||
cipher.init(2, aesKey, new IvParameterSpec(iv));
|
||||
this.cipherInputStream = new CipherInputStream(in, cipher);
|
||||
this.isInitialized = true;
|
||||
return this.cipherInputStream;
|
||||
} catch (GeneralSecurityException generalSecurityException) {
|
||||
IOException ioe = new IOException("Decryption error (do you have the JCE Unlimited Strength Jurisdiction Policy Files installed?)");
|
||||
ioe.initCause(generalSecurityException);
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
return init().read();
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
return init().read(b, off, len);
|
||||
}
|
||||
|
||||
public void close() {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package org.apache.commons.compress.archivers.sevenz;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
class Archive {
|
||||
long packPos;
|
||||
|
||||
long[] packSizes;
|
||||
|
||||
BitSet packCrcsDefined;
|
||||
|
||||
long[] packCrcs;
|
||||
|
||||
Folder[] folders;
|
||||
|
||||
SubStreamsInfo subStreamsInfo;
|
||||
|
||||
SevenZArchiveEntry[] files;
|
||||
|
||||
StreamMap streamMap;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package org.apache.commons.compress.archivers.sevenz;
|
||||
|
||||
class BindPair {
|
||||
long inIndex;
|
||||
|
||||
long outIndex;
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package org.apache.commons.compress.archivers.sevenz;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
class BoundedRandomAccessFileInputStream extends InputStream {
|
||||
private final RandomAccessFile file;
|
||||
|
||||
private long bytesRemaining;
|
||||
|
||||
public BoundedRandomAccessFileInputStream(RandomAccessFile file, long size) {
|
||||
this.file = file;
|
||||
this.bytesRemaining = size;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
if (this.bytesRemaining > 0L) {
|
||||
this.bytesRemaining--;
|
||||
return this.file.read();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
if (this.bytesRemaining == 0L)
|
||||
return -1;
|
||||
int bytesToRead = len;
|
||||
if ((long)bytesToRead > this.bytesRemaining)
|
||||
bytesToRead = (int)this.bytesRemaining;
|
||||
int bytesRead = this.file.read(b, off, bytesToRead);
|
||||
if (bytesRead >= 0)
|
||||
this.bytesRemaining -= (long)bytesRead;
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
public void close() {}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package org.apache.commons.compress.archivers.sevenz;
|
||||
|
||||
class Coder {
|
||||
byte[] decompressionMethodId;
|
||||
|
||||
long numInStreams;
|
||||
|
||||
long numOutStreams;
|
||||
|
||||
byte[] properties = null;
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package org.apache.commons.compress.archivers.sevenz;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
abstract class CoderBase {
|
||||
private final Class<?>[] acceptableOptions;
|
||||
|
||||
private static final byte[] NONE = new byte[0];
|
||||
|
||||
protected CoderBase(Class<?>... acceptableOptions) {
|
||||
this.acceptableOptions = acceptableOptions;
|
||||
}
|
||||
|
||||
boolean canAcceptOptions(Object opts) {
|
||||
for (Class<?> c : this.acceptableOptions) {
|
||||
if (c.isInstance(opts))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] getOptionsAsProperties(Object options) {
|
||||
return NONE;
|
||||
}
|
||||
|
||||
Object getOptionsFromCoder(Coder coder, InputStream in) {
|
||||
return null;
|
||||
}
|
||||
|
||||
abstract InputStream decode(InputStream paramInputStream, Coder paramCoder, byte[] paramArrayOfbyte) throws IOException;
|
||||
|
||||
OutputStream encode(OutputStream out, Object options) throws IOException {
|
||||
throw new UnsupportedOperationException("method doesn't support writing");
|
||||
}
|
||||
|
||||
protected static int numberOptionOrDefault(Object options, int defaultValue) {
|
||||
return (options instanceof Number) ? ((Number)options).intValue() : defaultValue;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
package org.apache.commons.compress.archivers.sevenz;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.Inflater;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
|
||||
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;
|
||||
import org.tukaani.xz.ARMOptions;
|
||||
import org.tukaani.xz.ARMThumbOptions;
|
||||
import org.tukaani.xz.FilterOptions;
|
||||
import org.tukaani.xz.FinishableOutputStream;
|
||||
import org.tukaani.xz.FinishableWrapperOutputStream;
|
||||
import org.tukaani.xz.IA64Options;
|
||||
import org.tukaani.xz.LZMAInputStream;
|
||||
import org.tukaani.xz.PowerPCOptions;
|
||||
import org.tukaani.xz.SPARCOptions;
|
||||
import org.tukaani.xz.X86Options;
|
||||
|
||||
class Coders {
|
||||
private static final Map<SevenZMethod, CoderBase> CODER_MAP = new HashMap<SevenZMethod, CoderBase>() {
|
||||
private static final long serialVersionUID = 1664829131806520867L;
|
||||
|
||||
{
|
||||
put(SevenZMethod.COPY, new Coders.CopyDecoder());
|
||||
put(SevenZMethod.LZMA, new Coders.LZMADecoder());
|
||||
put(SevenZMethod.LZMA2, new LZMA2Decoder());
|
||||
put(SevenZMethod.DEFLATE, new Coders.DeflateDecoder());
|
||||
put(SevenZMethod.BZIP2, new Coders.BZIP2Decoder());
|
||||
put(SevenZMethod.AES256SHA256, new AES256SHA256Decoder());
|
||||
put(SevenZMethod.BCJ_X86_FILTER, new Coders.BCJDecoder(new X86Options()));
|
||||
put(SevenZMethod.BCJ_PPC_FILTER, new Coders.BCJDecoder(new PowerPCOptions()));
|
||||
put(SevenZMethod.BCJ_IA64_FILTER, new Coders.BCJDecoder(new IA64Options()));
|
||||
put(SevenZMethod.BCJ_ARM_FILTER, new Coders.BCJDecoder(new ARMOptions()));
|
||||
put(SevenZMethod.BCJ_ARM_THUMB_FILTER, new Coders.BCJDecoder(new ARMThumbOptions()));
|
||||
put(SevenZMethod.BCJ_SPARC_FILTER, new Coders.BCJDecoder(new SPARCOptions()));
|
||||
put(SevenZMethod.DELTA_FILTER, new DeltaDecoder());
|
||||
}
|
||||
};
|
||||
|
||||
static CoderBase findByMethod(SevenZMethod method) {
|
||||
return CODER_MAP.get(method);
|
||||
}
|
||||
|
||||
static InputStream addDecoder(InputStream is, Coder coder, byte[] password) throws IOException {
|
||||
CoderBase cb = findByMethod(SevenZMethod.byId(coder.decompressionMethodId));
|
||||
if (cb == null)
|
||||
throw new IOException("Unsupported compression method " + Arrays.toString(coder.decompressionMethodId));
|
||||
return cb.decode(is, coder, password);
|
||||
}
|
||||
|
||||
static OutputStream addEncoder(OutputStream out, SevenZMethod method, Object options) throws IOException {
|
||||
CoderBase cb = findByMethod(method);
|
||||
if (cb == null)
|
||||
throw new IOException("Unsupported compression method " + method);
|
||||
return cb.encode(out, options);
|
||||
}
|
||||
|
||||
static class CopyDecoder extends CoderBase {
|
||||
CopyDecoder() {
|
||||
super(new Class<?>[0]);
|
||||
}
|
||||
|
||||
InputStream decode(InputStream in, Coder coder, byte[] password) throws IOException {
|
||||
return in;
|
||||
}
|
||||
|
||||
OutputStream encode(OutputStream out, Object options) {
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
static class LZMADecoder extends CoderBase {
|
||||
LZMADecoder() {
|
||||
super(new Class<?>[0]);
|
||||
}
|
||||
|
||||
InputStream decode(InputStream in, Coder coder, byte[] password) throws IOException {
|
||||
byte propsByte = coder.properties[0];
|
||||
long dictSize = (long)coder.properties[1];
|
||||
for (int i = 1; i < 4; i++)
|
||||
dictSize |= ((long)coder.properties[i + 1] & 0xFFL) << 8 * i;
|
||||
if (dictSize > 2147483632L)
|
||||
throw new IOException("Dictionary larger than 4GiB maximum size");
|
||||
return new LZMAInputStream(in, -1L, propsByte, (int)dictSize);
|
||||
}
|
||||
}
|
||||
|
||||
static class BCJDecoder extends CoderBase {
|
||||
private final FilterOptions opts;
|
||||
|
||||
BCJDecoder(FilterOptions opts) {
|
||||
super(new Class<?>[0]);
|
||||
this.opts = opts;
|
||||
}
|
||||
|
||||
InputStream decode(InputStream in, Coder coder, byte[] password) throws IOException {
|
||||
try {
|
||||
return this.opts.getInputStream(in);
|
||||
} catch (AssertionError e) {
|
||||
IOException ex = new IOException("BCJ filter needs XZ for Java > 1.4 - see http://commons.apache.org/proper/commons-compress/limitations.html#7Z");
|
||||
ex.initCause(e);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
OutputStream encode(OutputStream out, Object options) {
|
||||
FinishableOutputStream fo = this.opts.getOutputStream(new FinishableWrapperOutputStream(out));
|
||||
return new FilterOutputStream((OutputStream)fo) {
|
||||
public void flush() {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static class DeflateDecoder extends CoderBase {
|
||||
DeflateDecoder() {
|
||||
super(new Class<?>[] { Number.class });
|
||||
}
|
||||
|
||||
InputStream decode(InputStream in, Coder coder, byte[] password) throws IOException {
|
||||
return new InflaterInputStream(new Coders.DummyByteAddingInputStream(in), new Inflater(true));
|
||||
}
|
||||
|
||||
OutputStream encode(OutputStream out, Object options) {
|
||||
int level = numberOptionOrDefault(options, 9);
|
||||
return new DeflaterOutputStream(out, new Deflater(level, true));
|
||||
}
|
||||
}
|
||||
|
||||
static class BZIP2Decoder extends CoderBase {
|
||||
BZIP2Decoder() {
|
||||
super(new Class<?>[] { Number.class });
|
||||
}
|
||||
|
||||
InputStream decode(InputStream in, Coder coder, byte[] password) throws IOException {
|
||||
return new BZip2CompressorInputStream(in);
|
||||
}
|
||||
|
||||
OutputStream encode(OutputStream out, Object options) throws IOException {
|
||||
int blockSize = numberOptionOrDefault(options, 9);
|
||||
return new BZip2CompressorOutputStream(out, blockSize);
|
||||
}
|
||||
}
|
||||
|
||||
private static class DummyByteAddingInputStream extends FilterInputStream {
|
||||
private boolean addDummyByte = true;
|
||||
|
||||
private DummyByteAddingInputStream(InputStream in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
int result = super.read();
|
||||
if (result == -1 && this.addDummyByte) {
|
||||
this.addDummyByte = false;
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
int result = super.read(b, off, len);
|
||||
if (result == -1 && this.addDummyByte) {
|
||||
this.addDummyByte = false;
|
||||
b[off] = 0;
|
||||
return 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package org.apache.commons.compress.archivers.sevenz;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import org.tukaani.xz.DeltaOptions;
|
||||
import org.tukaani.xz.FinishableWrapperOutputStream;
|
||||
import org.tukaani.xz.UnsupportedOptionsException;
|
||||
|
||||
class DeltaDecoder extends CoderBase {
|
||||
DeltaDecoder() {
|
||||
super(new Class<?>[] { Number.class });
|
||||
}
|
||||
|
||||
InputStream decode(InputStream in, Coder coder, byte[] password) throws IOException {
|
||||
return new DeltaOptions(getOptionsFromCoder(coder)).getInputStream(in);
|
||||
}
|
||||
|
||||
OutputStream encode(OutputStream out, Object options) throws IOException {
|
||||
int distance = numberOptionOrDefault(options, 1);
|
||||
try {
|
||||
return (OutputStream)new DeltaOptions(distance).getOutputStream(new FinishableWrapperOutputStream(out));
|
||||
} catch (UnsupportedOptionsException ex) {
|
||||
throw new IOException(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
byte[] getOptionsAsProperties(Object options) {
|
||||
return new byte[] { (byte)(numberOptionOrDefault(options, 1) - 1) };
|
||||
}
|
||||
|
||||
Object getOptionsFromCoder(Coder coder, InputStream in) {
|
||||
return getOptionsFromCoder(coder);
|
||||
}
|
||||
|
||||
private int getOptionsFromCoder(Coder coder) {
|
||||
if (coder.properties == null || coder.properties.length == 0)
|
||||
return 1;
|
||||
return (0xFF & coder.properties[0]) + 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package org.apache.commons.compress.archivers.sevenz;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
class Folder {
|
||||
Coder[] coders;
|
||||
|
||||
long totalInputStreams;
|
||||
|
||||
long totalOutputStreams;
|
||||
|
||||
BindPair[] bindPairs;
|
||||
|
||||
long[] packedStreams;
|
||||
|
||||
long[] unpackSizes;
|
||||
|
||||
boolean hasCrc;
|
||||
|
||||
long crc;
|
||||
|
||||
int numUnpackSubStreams;
|
||||
|
||||
Iterable<Coder> getOrderedCoders() {
|
||||
LinkedList<Coder> l = new LinkedList<Coder>();
|
||||
int current = (int)this.packedStreams[0];
|
||||
while (current != -1) {
|
||||
l.addLast(this.coders[current]);
|
||||
int pair = findBindPairForOutStream(current);
|
||||
current = (pair != -1) ? (int)(this.bindPairs[pair]).inIndex : -1;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
int findBindPairForInStream(int index) {
|
||||
for (int i = 0; i < this.bindPairs.length; i++) {
|
||||
if ((this.bindPairs[i]).inIndex == (long)index)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int findBindPairForOutStream(int index) {
|
||||
for (int i = 0; i < this.bindPairs.length; i++) {
|
||||
if ((this.bindPairs[i]).outIndex == (long)index)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
long getUnpackSize() {
|
||||
if (this.totalOutputStreams == 0L)
|
||||
return 0L;
|
||||
for (int i = (int)this.totalOutputStreams - 1; i >= 0; i--) {
|
||||
if (findBindPairForOutStream(i) < 0)
|
||||
return this.unpackSizes[i];
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package org.apache.commons.compress.archivers.sevenz;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import org.tukaani.xz.FinishableOutputStream;
|
||||
import org.tukaani.xz.FinishableWrapperOutputStream;
|
||||
import org.tukaani.xz.LZMA2InputStream;
|
||||
import org.tukaani.xz.LZMA2Options;
|
||||
|
||||
class LZMA2Decoder extends CoderBase {
|
||||
LZMA2Decoder() {
|
||||
super(new Class<?>[] { LZMA2Options.class, Number.class });
|
||||
}
|
||||
|
||||
InputStream decode(InputStream in, Coder coder, byte[] password) throws IOException {
|
||||
try {
|
||||
int dictionarySize = getDictionarySize(coder);
|
||||
return new LZMA2InputStream(in, dictionarySize);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new IOException(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
OutputStream encode(OutputStream out, Object opts) throws IOException {
|
||||
LZMA2Options options = getOptions(opts);
|
||||
FinishableWrapperOutputStream finishableWrapperOutputStream = new FinishableWrapperOutputStream(out);
|
||||
return (OutputStream)options.getOutputStream((FinishableOutputStream)finishableWrapperOutputStream);
|
||||
}
|
||||
|
||||
byte[] getOptionsAsProperties(Object opts) {
|
||||
int dictSize = getDictSize(opts);
|
||||
int lead = Integer.numberOfLeadingZeros(dictSize);
|
||||
int secondBit = (dictSize >>> 30 - lead) - 2;
|
||||
return new byte[] { (byte)((19 - lead) * 2 + secondBit) };
|
||||
}
|
||||
|
||||
Object getOptionsFromCoder(Coder coder, InputStream in) {
|
||||
return getDictionarySize(coder);
|
||||
}
|
||||
|
||||
private int getDictSize(Object opts) {
|
||||
if (opts instanceof LZMA2Options)
|
||||
return ((LZMA2Options)opts).getDictSize();
|
||||
return numberOptionOrDefault(opts);
|
||||
}
|
||||
|
||||
private int getDictionarySize(Coder coder) throws IllegalArgumentException {
|
||||
int dictionarySizeBits = 0xFF & coder.properties[0];
|
||||
if ((dictionarySizeBits & 0xFFFFFFC0) != 0)
|
||||
throw new IllegalArgumentException("Unsupported LZMA2 property bits");
|
||||
if (dictionarySizeBits > 40)
|
||||
throw new IllegalArgumentException("Dictionary larger than 4GiB maximum size");
|
||||
if (dictionarySizeBits == 40)
|
||||
return -1;
|
||||
return (0x2 | dictionarySizeBits & 0x1) << dictionarySizeBits / 2 + 11;
|
||||
}
|
||||
|
||||
private LZMA2Options getOptions(Object opts) throws IOException {
|
||||
if (opts instanceof LZMA2Options)
|
||||
return (LZMA2Options)opts;
|
||||
LZMA2Options options = new LZMA2Options();
|
||||
options.setDictSize(numberOptionOrDefault(opts));
|
||||
return options;
|
||||
}
|
||||
|
||||
private int numberOptionOrDefault(Object opts) {
|
||||
return numberOptionOrDefault(opts, 8388608);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package org.apache.commons.compress.archivers.sevenz;
|
||||
|
||||
final class NID {
|
||||
public static final int kEnd = 0;
|
||||
|
||||
public static final int kHeader = 1;
|
||||
|
||||
public static final int kArchiveProperties = 2;
|
||||
|
||||
public static final int kAdditionalStreamsInfo = 3;
|
||||
|
||||
public static final int kMainStreamsInfo = 4;
|
||||
|
||||
public static final int kFilesInfo = 5;
|
||||
|
||||
public static final int kPackInfo = 6;
|
||||
|
||||
public static final int kUnpackInfo = 7;
|
||||
|
||||
public static final int kSubStreamsInfo = 8;
|
||||
|
||||
public static final int kSize = 9;
|
||||
|
||||
public static final int kCRC = 10;
|
||||
|
||||
public static final int kFolder = 11;
|
||||
|
||||
public static final int kCodersUnpackSize = 12;
|
||||
|
||||
public static final int kNumUnpackStream = 13;
|
||||
|
||||
public static final int kEmptyStream = 14;
|
||||
|
||||
public static final int kEmptyFile = 15;
|
||||
|
||||
public static final int kAnti = 16;
|
||||
|
||||
public static final int kName = 17;
|
||||
|
||||
public static final int kCTime = 18;
|
||||
|
||||
public static final int kATime = 19;
|
||||
|
||||
public static final int kMTime = 20;
|
||||
|
||||
public static final int kWinAttributes = 21;
|
||||
|
||||
public static final int kComment = 22;
|
||||
|
||||
public static final int kEncodedHeader = 23;
|
||||
|
||||
public static final int kStartPos = 24;
|
||||
|
||||
public static final int kDummy = 25;
|
||||
}
|
||||
|
|
@ -0,0 +1,258 @@
|
|||
package org.apache.commons.compress.archivers.sevenz;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.TimeZone;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
|
||||
public class SevenZArchiveEntry implements ArchiveEntry {
|
||||
private String name;
|
||||
|
||||
private boolean hasStream;
|
||||
|
||||
private boolean isDirectory;
|
||||
|
||||
private boolean isAntiItem;
|
||||
|
||||
private boolean hasCreationDate;
|
||||
|
||||
private boolean hasLastModifiedDate;
|
||||
|
||||
private boolean hasAccessDate;
|
||||
|
||||
private long creationDate;
|
||||
|
||||
private long lastModifiedDate;
|
||||
|
||||
private long accessDate;
|
||||
|
||||
private boolean hasWindowsAttributes;
|
||||
|
||||
private int windowsAttributes;
|
||||
|
||||
private boolean hasCrc;
|
||||
|
||||
private long crc;
|
||||
|
||||
private long compressedCrc;
|
||||
|
||||
private long size;
|
||||
|
||||
private long compressedSize;
|
||||
|
||||
private Iterable<? extends SevenZMethodConfiguration> contentMethods;
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean hasStream() {
|
||||
return this.hasStream;
|
||||
}
|
||||
|
||||
public void setHasStream(boolean hasStream) {
|
||||
this.hasStream = hasStream;
|
||||
}
|
||||
|
||||
public boolean isDirectory() {
|
||||
return this.isDirectory;
|
||||
}
|
||||
|
||||
public void setDirectory(boolean isDirectory) {
|
||||
this.isDirectory = isDirectory;
|
||||
}
|
||||
|
||||
public boolean isAntiItem() {
|
||||
return this.isAntiItem;
|
||||
}
|
||||
|
||||
public void setAntiItem(boolean isAntiItem) {
|
||||
this.isAntiItem = isAntiItem;
|
||||
}
|
||||
|
||||
public boolean getHasCreationDate() {
|
||||
return this.hasCreationDate;
|
||||
}
|
||||
|
||||
public void setHasCreationDate(boolean hasCreationDate) {
|
||||
this.hasCreationDate = hasCreationDate;
|
||||
}
|
||||
|
||||
public Date getCreationDate() {
|
||||
if (this.hasCreationDate)
|
||||
return ntfsTimeToJavaTime(this.creationDate);
|
||||
throw new UnsupportedOperationException("The entry doesn't have this timestamp");
|
||||
}
|
||||
|
||||
public void setCreationDate(long ntfsCreationDate) {
|
||||
this.creationDate = ntfsCreationDate;
|
||||
}
|
||||
|
||||
public void setCreationDate(Date creationDate) {
|
||||
this.hasCreationDate = (creationDate != null);
|
||||
if (this.hasCreationDate)
|
||||
this.creationDate = javaTimeToNtfsTime(creationDate);
|
||||
}
|
||||
|
||||
public boolean getHasLastModifiedDate() {
|
||||
return this.hasLastModifiedDate;
|
||||
}
|
||||
|
||||
public void setHasLastModifiedDate(boolean hasLastModifiedDate) {
|
||||
this.hasLastModifiedDate = hasLastModifiedDate;
|
||||
}
|
||||
|
||||
public Date getLastModifiedDate() {
|
||||
if (this.hasLastModifiedDate)
|
||||
return ntfsTimeToJavaTime(this.lastModifiedDate);
|
||||
throw new UnsupportedOperationException("The entry doesn't have this timestamp");
|
||||
}
|
||||
|
||||
public void setLastModifiedDate(long ntfsLastModifiedDate) {
|
||||
this.lastModifiedDate = ntfsLastModifiedDate;
|
||||
}
|
||||
|
||||
public void setLastModifiedDate(Date lastModifiedDate) {
|
||||
this.hasLastModifiedDate = (lastModifiedDate != null);
|
||||
if (this.hasLastModifiedDate)
|
||||
this.lastModifiedDate = javaTimeToNtfsTime(lastModifiedDate);
|
||||
}
|
||||
|
||||
public boolean getHasAccessDate() {
|
||||
return this.hasAccessDate;
|
||||
}
|
||||
|
||||
public void setHasAccessDate(boolean hasAcessDate) {
|
||||
this.hasAccessDate = hasAcessDate;
|
||||
}
|
||||
|
||||
public Date getAccessDate() {
|
||||
if (this.hasAccessDate)
|
||||
return ntfsTimeToJavaTime(this.accessDate);
|
||||
throw new UnsupportedOperationException("The entry doesn't have this timestamp");
|
||||
}
|
||||
|
||||
public void setAccessDate(long ntfsAccessDate) {
|
||||
this.accessDate = ntfsAccessDate;
|
||||
}
|
||||
|
||||
public void setAccessDate(Date accessDate) {
|
||||
this.hasAccessDate = (accessDate != null);
|
||||
if (this.hasAccessDate)
|
||||
this.accessDate = javaTimeToNtfsTime(accessDate);
|
||||
}
|
||||
|
||||
public boolean getHasWindowsAttributes() {
|
||||
return this.hasWindowsAttributes;
|
||||
}
|
||||
|
||||
public void setHasWindowsAttributes(boolean hasWindowsAttributes) {
|
||||
this.hasWindowsAttributes = hasWindowsAttributes;
|
||||
}
|
||||
|
||||
public int getWindowsAttributes() {
|
||||
return this.windowsAttributes;
|
||||
}
|
||||
|
||||
public void setWindowsAttributes(int windowsAttributes) {
|
||||
this.windowsAttributes = windowsAttributes;
|
||||
}
|
||||
|
||||
public boolean getHasCrc() {
|
||||
return this.hasCrc;
|
||||
}
|
||||
|
||||
public void setHasCrc(boolean hasCrc) {
|
||||
this.hasCrc = hasCrc;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getCrc() {
|
||||
return (int)this.crc;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setCrc(int crc) {
|
||||
this.crc = (long)crc;
|
||||
}
|
||||
|
||||
public long getCrcValue() {
|
||||
return this.crc;
|
||||
}
|
||||
|
||||
public void setCrcValue(long crc) {
|
||||
this.crc = crc;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
int getCompressedCrc() {
|
||||
return (int)this.compressedCrc;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
void setCompressedCrc(int crc) {
|
||||
this.compressedCrc = (long)crc;
|
||||
}
|
||||
|
||||
long getCompressedCrcValue() {
|
||||
return this.compressedCrc;
|
||||
}
|
||||
|
||||
void setCompressedCrcValue(long crc) {
|
||||
this.compressedCrc = crc;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public void setSize(long size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
long getCompressedSize() {
|
||||
return this.compressedSize;
|
||||
}
|
||||
|
||||
void setCompressedSize(long size) {
|
||||
this.compressedSize = size;
|
||||
}
|
||||
|
||||
public void setContentMethods(Iterable<? extends SevenZMethodConfiguration> methods) {
|
||||
if (methods != null) {
|
||||
LinkedList<SevenZMethodConfiguration> l = new LinkedList<SevenZMethodConfiguration>();
|
||||
for (SevenZMethodConfiguration m : methods)
|
||||
l.addLast(m);
|
||||
this.contentMethods = Collections.<SevenZMethodConfiguration>unmodifiableList(l);
|
||||
} else {
|
||||
this.contentMethods = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Iterable<? extends SevenZMethodConfiguration> getContentMethods() {
|
||||
return this.contentMethods;
|
||||
}
|
||||
|
||||
public static Date ntfsTimeToJavaTime(long ntfsTime) {
|
||||
Calendar ntfsEpoch = Calendar.getInstance();
|
||||
ntfsEpoch.setTimeZone(TimeZone.getTimeZone("GMT+0"));
|
||||
ntfsEpoch.set(1601, 0, 1, 0, 0, 0);
|
||||
ntfsEpoch.set(14, 0);
|
||||
long realTime = ntfsEpoch.getTimeInMillis() + ntfsTime / 10000L;
|
||||
return new Date(realTime);
|
||||
}
|
||||
|
||||
public static long javaTimeToNtfsTime(Date date) {
|
||||
Calendar ntfsEpoch = Calendar.getInstance();
|
||||
ntfsEpoch.setTimeZone(TimeZone.getTimeZone("GMT+0"));
|
||||
ntfsEpoch.set(1601, 0, 1, 0, 0, 0);
|
||||
ntfsEpoch.set(14, 0);
|
||||
return (date.getTime() - ntfsEpoch.getTimeInMillis()) * 1000L * 10L;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,692 @@
|
|||
package org.apache.commons.compress.archivers.sevenz;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.zip.CRC32;
|
||||
import org.apache.commons.compress.utils.BoundedInputStream;
|
||||
import org.apache.commons.compress.utils.CRC32VerifyingInputStream;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
|
||||
public class SevenZFile implements Closeable {
|
||||
static final int SIGNATURE_HEADER_SIZE = 32;
|
||||
|
||||
private RandomAccessFile file;
|
||||
|
||||
private final Archive archive;
|
||||
|
||||
private int currentEntryIndex = -1;
|
||||
|
||||
private int currentFolderIndex = -1;
|
||||
|
||||
private InputStream currentFolderInputStream = null;
|
||||
|
||||
private InputStream currentEntryInputStream = null;
|
||||
|
||||
private byte[] password;
|
||||
|
||||
static final byte[] sevenZSignature = new byte[] { 55, 122, -68, -81, 39, 28 };
|
||||
|
||||
public SevenZFile(File filename, byte[] password) throws IOException {
|
||||
boolean succeeded = false;
|
||||
this.file = new RandomAccessFile(filename, "r");
|
||||
try {
|
||||
this.archive = readHeaders(password);
|
||||
if (password != null) {
|
||||
this.password = new byte[password.length];
|
||||
System.arraycopy(password, 0, this.password, 0, password.length);
|
||||
} else {
|
||||
this.password = null;
|
||||
}
|
||||
succeeded = true;
|
||||
} finally {
|
||||
if (!succeeded)
|
||||
this.file.close();
|
||||
}
|
||||
}
|
||||
|
||||
public SevenZFile(File filename) throws IOException {
|
||||
this(filename, null);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (this.file != null)
|
||||
try {
|
||||
this.file.close();
|
||||
} finally {
|
||||
this.file = null;
|
||||
if (this.password != null)
|
||||
Arrays.fill(this.password, (byte)0);
|
||||
this.password = null;
|
||||
}
|
||||
}
|
||||
|
||||
public SevenZArchiveEntry getNextEntry() throws IOException {
|
||||
if (this.currentEntryIndex >= this.archive.files.length - 1)
|
||||
return null;
|
||||
this.currentEntryIndex++;
|
||||
SevenZArchiveEntry entry = this.archive.files[this.currentEntryIndex];
|
||||
buildDecodingStream();
|
||||
return entry;
|
||||
}
|
||||
|
||||
private Archive readHeaders(byte[] password) throws IOException {
|
||||
byte[] signature = new byte[6];
|
||||
this.file.readFully(signature);
|
||||
if (!Arrays.equals(signature, sevenZSignature))
|
||||
throw new IOException("Bad 7z signature");
|
||||
byte archiveVersionMajor = this.file.readByte();
|
||||
byte archiveVersionMinor = this.file.readByte();
|
||||
if (archiveVersionMajor != 0)
|
||||
throw new IOException(String.format("Unsupported 7z version (%d,%d)", archiveVersionMajor, archiveVersionMinor));
|
||||
long startHeaderCrc = 0xFFFFFFFFL & (long)Integer.reverseBytes(this.file.readInt());
|
||||
StartHeader startHeader = readStartHeader(startHeaderCrc);
|
||||
int nextHeaderSizeInt = (int)startHeader.nextHeaderSize;
|
||||
if ((long)nextHeaderSizeInt != startHeader.nextHeaderSize)
|
||||
throw new IOException("cannot handle nextHeaderSize " + startHeader.nextHeaderSize);
|
||||
this.file.seek(32L + startHeader.nextHeaderOffset);
|
||||
byte[] nextHeader = new byte[nextHeaderSizeInt];
|
||||
this.file.readFully(nextHeader);
|
||||
CRC32 crc = new CRC32();
|
||||
crc.update(nextHeader);
|
||||
if (startHeader.nextHeaderCrc != crc.getValue())
|
||||
throw new IOException("NextHeader CRC mismatch");
|
||||
ByteArrayInputStream byteStream = new ByteArrayInputStream(nextHeader);
|
||||
DataInputStream nextHeaderInputStream = new DataInputStream(byteStream);
|
||||
Archive archive = new Archive();
|
||||
int nid = nextHeaderInputStream.readUnsignedByte();
|
||||
if (nid == 23) {
|
||||
nextHeaderInputStream = readEncodedHeader(nextHeaderInputStream, archive, password);
|
||||
archive = new Archive();
|
||||
nid = nextHeaderInputStream.readUnsignedByte();
|
||||
}
|
||||
if (nid == 1) {
|
||||
readHeader(nextHeaderInputStream, archive);
|
||||
nextHeaderInputStream.close();
|
||||
} else {
|
||||
throw new IOException("Broken or unsupported archive: no Header");
|
||||
}
|
||||
return archive;
|
||||
}
|
||||
|
||||
private StartHeader readStartHeader(long startHeaderCrc) throws IOException {
|
||||
StartHeader startHeader = new StartHeader();
|
||||
DataInputStream dataInputStream = null;
|
||||
try {
|
||||
dataInputStream = new DataInputStream(new CRC32VerifyingInputStream((InputStream)new BoundedRandomAccessFileInputStream(this.file, 20L), 20L, startHeaderCrc));
|
||||
startHeader.nextHeaderOffset = Long.reverseBytes(dataInputStream.readLong());
|
||||
startHeader.nextHeaderSize = Long.reverseBytes(dataInputStream.readLong());
|
||||
startHeader.nextHeaderCrc = 0xFFFFFFFFL & (long)Integer.reverseBytes(dataInputStream.readInt());
|
||||
return startHeader;
|
||||
} finally {
|
||||
if (dataInputStream != null)
|
||||
dataInputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void readHeader(DataInput header, Archive archive) throws IOException {
|
||||
int nid = header.readUnsignedByte();
|
||||
if (nid == 2) {
|
||||
readArchiveProperties(header);
|
||||
nid = header.readUnsignedByte();
|
||||
}
|
||||
if (nid == 3)
|
||||
throw new IOException("Additional streams unsupported");
|
||||
if (nid == 4) {
|
||||
readStreamsInfo(header, archive);
|
||||
nid = header.readUnsignedByte();
|
||||
}
|
||||
if (nid == 5) {
|
||||
readFilesInfo(header, archive);
|
||||
nid = header.readUnsignedByte();
|
||||
}
|
||||
if (nid != 0)
|
||||
throw new IOException("Badly terminated header");
|
||||
}
|
||||
|
||||
private void readArchiveProperties(DataInput input) throws IOException {
|
||||
int nid = input.readUnsignedByte();
|
||||
while (nid != 0) {
|
||||
long propertySize = readUint64(input);
|
||||
byte[] property = new byte[(int)propertySize];
|
||||
input.readFully(property);
|
||||
nid = input.readUnsignedByte();
|
||||
}
|
||||
}
|
||||
|
||||
private DataInputStream readEncodedHeader(DataInputStream header, Archive archive, byte[] password) throws IOException {
|
||||
readStreamsInfo(header, archive);
|
||||
Folder folder = archive.folders[0];
|
||||
int firstPackStreamIndex = 0;
|
||||
long folderOffset = 32L + archive.packPos + 0L;
|
||||
this.file.seek(folderOffset);
|
||||
InputStream inputStreamStack = new BoundedRandomAccessFileInputStream(this.file, archive.packSizes[0]);
|
||||
for (Coder coder : folder.getOrderedCoders()) {
|
||||
if (coder.numInStreams != 1L || coder.numOutStreams != 1L)
|
||||
throw new IOException("Multi input/output stream coders are not yet supported");
|
||||
inputStreamStack = Coders.addDecoder(inputStreamStack, coder, password);
|
||||
}
|
||||
if (folder.hasCrc)
|
||||
inputStreamStack = new CRC32VerifyingInputStream(inputStreamStack, folder.getUnpackSize(), folder.crc);
|
||||
byte[] nextHeader = new byte[(int)folder.getUnpackSize()];
|
||||
DataInputStream nextHeaderInputStream = new DataInputStream(inputStreamStack);
|
||||
try {
|
||||
nextHeaderInputStream.readFully(nextHeader);
|
||||
} finally {
|
||||
nextHeaderInputStream.close();
|
||||
}
|
||||
return new DataInputStream(new ByteArrayInputStream(nextHeader));
|
||||
}
|
||||
|
||||
private void readStreamsInfo(DataInput header, Archive archive) throws IOException {
|
||||
int nid = header.readUnsignedByte();
|
||||
if (nid == 6) {
|
||||
readPackInfo(header, archive);
|
||||
nid = header.readUnsignedByte();
|
||||
}
|
||||
if (nid == 7) {
|
||||
readUnpackInfo(header, archive);
|
||||
nid = header.readUnsignedByte();
|
||||
} else {
|
||||
archive.folders = new Folder[0];
|
||||
}
|
||||
if (nid == 8) {
|
||||
readSubStreamsInfo(header, archive);
|
||||
nid = header.readUnsignedByte();
|
||||
}
|
||||
if (nid != 0)
|
||||
throw new IOException("Badly terminated StreamsInfo");
|
||||
}
|
||||
|
||||
private void readPackInfo(DataInput header, Archive archive) throws IOException {
|
||||
archive.packPos = readUint64(header);
|
||||
long numPackStreams = readUint64(header);
|
||||
int nid = header.readUnsignedByte();
|
||||
if (nid == 9) {
|
||||
archive.packSizes = new long[(int)numPackStreams];
|
||||
for (int i = 0; i < archive.packSizes.length; i++)
|
||||
archive.packSizes[i] = readUint64(header);
|
||||
nid = header.readUnsignedByte();
|
||||
}
|
||||
if (nid == 10) {
|
||||
archive.packCrcsDefined = readAllOrBits(header, (int)numPackStreams);
|
||||
archive.packCrcs = new long[(int)numPackStreams];
|
||||
for (int i = 0; i < (int)numPackStreams; i++) {
|
||||
if (archive.packCrcsDefined.get(i))
|
||||
archive.packCrcs[i] = 0xFFFFFFFFL & (long)Integer.reverseBytes(header.readInt());
|
||||
}
|
||||
nid = header.readUnsignedByte();
|
||||
}
|
||||
if (nid != 0)
|
||||
throw new IOException("Badly terminated PackInfo (" + nid + ")");
|
||||
}
|
||||
|
||||
private void readUnpackInfo(DataInput header, Archive archive) throws IOException {
|
||||
int nid = header.readUnsignedByte();
|
||||
if (nid != 11)
|
||||
throw new IOException("Expected kFolder, got " + nid);
|
||||
long numFolders = readUint64(header);
|
||||
Folder[] folders = new Folder[(int)numFolders];
|
||||
archive.folders = folders;
|
||||
int external = header.readUnsignedByte();
|
||||
if (external != 0)
|
||||
throw new IOException("External unsupported");
|
||||
for (int i = 0; i < (int)numFolders; i++)
|
||||
folders[i] = readFolder(header);
|
||||
nid = header.readUnsignedByte();
|
||||
if (nid != 12)
|
||||
throw new IOException("Expected kCodersUnpackSize, got " + nid);
|
||||
for (Folder folder : folders) {
|
||||
folder.unpackSizes = new long[(int)folder.totalOutputStreams];
|
||||
for (int j = 0; (long)j < folder.totalOutputStreams; j++)
|
||||
folder.unpackSizes[j] = readUint64(header);
|
||||
}
|
||||
nid = header.readUnsignedByte();
|
||||
if (nid == 10) {
|
||||
BitSet crcsDefined = readAllOrBits(header, (int)numFolders);
|
||||
for (int j = 0; j < (int)numFolders; j++) {
|
||||
if (crcsDefined.get(j)) {
|
||||
(folders[j]).hasCrc = true;
|
||||
(folders[j]).crc = 0xFFFFFFFFL & (long)Integer.reverseBytes(header.readInt());
|
||||
} else {
|
||||
(folders[j]).hasCrc = false;
|
||||
}
|
||||
}
|
||||
nid = header.readUnsignedByte();
|
||||
}
|
||||
if (nid != 0)
|
||||
throw new IOException("Badly terminated UnpackInfo");
|
||||
}
|
||||
|
||||
private void readSubStreamsInfo(DataInput header, Archive archive) throws IOException {
|
||||
for (Folder folder : archive.folders)
|
||||
folder.numUnpackSubStreams = 1;
|
||||
int totalUnpackStreams = archive.folders.length;
|
||||
int nid = header.readUnsignedByte();
|
||||
if (nid == 13) {
|
||||
totalUnpackStreams = 0;
|
||||
for (Folder folder : archive.folders) {
|
||||
long numStreams = readUint64(header);
|
||||
folder.numUnpackSubStreams = (int)numStreams;
|
||||
totalUnpackStreams = (int)((long)totalUnpackStreams + numStreams);
|
||||
}
|
||||
nid = header.readUnsignedByte();
|
||||
}
|
||||
SubStreamsInfo subStreamsInfo = new SubStreamsInfo();
|
||||
subStreamsInfo.unpackSizes = new long[totalUnpackStreams];
|
||||
subStreamsInfo.hasCrc = new BitSet(totalUnpackStreams);
|
||||
subStreamsInfo.crcs = new long[totalUnpackStreams];
|
||||
int nextUnpackStream = 0;
|
||||
for (Folder folder : archive.folders) {
|
||||
if (folder.numUnpackSubStreams != 0) {
|
||||
long sum = 0L;
|
||||
if (nid == 9)
|
||||
for (int i = 0; i < folder.numUnpackSubStreams - 1; i++) {
|
||||
long size = readUint64(header);
|
||||
subStreamsInfo.unpackSizes[nextUnpackStream++] = size;
|
||||
sum += size;
|
||||
}
|
||||
subStreamsInfo.unpackSizes[nextUnpackStream++] = folder.getUnpackSize() - sum;
|
||||
}
|
||||
}
|
||||
if (nid == 9)
|
||||
nid = header.readUnsignedByte();
|
||||
int numDigests = 0;
|
||||
for (Folder folder : archive.folders) {
|
||||
if (folder.numUnpackSubStreams != 1 || !folder.hasCrc)
|
||||
numDigests += folder.numUnpackSubStreams;
|
||||
}
|
||||
if (nid == 10) {
|
||||
BitSet hasMissingCrc = readAllOrBits(header, numDigests);
|
||||
long[] missingCrcs = new long[numDigests];
|
||||
for (int i = 0; i < numDigests; i++) {
|
||||
if (hasMissingCrc.get(i))
|
||||
missingCrcs[i] = 0xFFFFFFFFL & (long)Integer.reverseBytes(header.readInt());
|
||||
}
|
||||
int nextCrc = 0;
|
||||
int nextMissingCrc = 0;
|
||||
for (Folder folder : archive.folders) {
|
||||
if (folder.numUnpackSubStreams == 1 && folder.hasCrc) {
|
||||
subStreamsInfo.hasCrc.set(nextCrc, true);
|
||||
subStreamsInfo.crcs[nextCrc] = folder.crc;
|
||||
nextCrc++;
|
||||
} else {
|
||||
for (int j = 0; j < folder.numUnpackSubStreams; j++) {
|
||||
subStreamsInfo.hasCrc.set(nextCrc, hasMissingCrc.get(nextMissingCrc));
|
||||
subStreamsInfo.crcs[nextCrc] = missingCrcs[nextMissingCrc];
|
||||
nextCrc++;
|
||||
nextMissingCrc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
nid = header.readUnsignedByte();
|
||||
}
|
||||
if (nid != 0)
|
||||
throw new IOException("Badly terminated SubStreamsInfo");
|
||||
archive.subStreamsInfo = subStreamsInfo;
|
||||
}
|
||||
|
||||
private Folder readFolder(DataInput header) throws IOException {
|
||||
Folder folder = new Folder();
|
||||
long numCoders = readUint64(header);
|
||||
Coder[] coders = new Coder[(int)numCoders];
|
||||
long totalInStreams = 0L;
|
||||
long totalOutStreams = 0L;
|
||||
for (int i = 0; i < coders.length; i++) {
|
||||
coders[i] = new Coder();
|
||||
int bits = header.readUnsignedByte();
|
||||
int idSize = bits & 0xF;
|
||||
boolean isSimple = ((bits & 0x10) == 0);
|
||||
boolean hasAttributes = ((bits & 0x20) != 0);
|
||||
boolean moreAlternativeMethods = ((bits & 0x80) != 0);
|
||||
(coders[i]).decompressionMethodId = new byte[idSize];
|
||||
header.readFully((coders[i]).decompressionMethodId);
|
||||
if (isSimple) {
|
||||
(coders[i]).numInStreams = 1L;
|
||||
(coders[i]).numOutStreams = 1L;
|
||||
} else {
|
||||
(coders[i]).numInStreams = readUint64(header);
|
||||
(coders[i]).numOutStreams = readUint64(header);
|
||||
}
|
||||
totalInStreams += (coders[i]).numInStreams;
|
||||
totalOutStreams += (coders[i]).numOutStreams;
|
||||
if (hasAttributes) {
|
||||
long propertiesSize = readUint64(header);
|
||||
(coders[i]).properties = new byte[(int)propertiesSize];
|
||||
header.readFully((coders[i]).properties);
|
||||
}
|
||||
if (moreAlternativeMethods)
|
||||
throw new IOException("Alternative methods are unsupported, please report. The reference implementation doesn't support them either.");
|
||||
}
|
||||
folder.coders = coders;
|
||||
folder.totalInputStreams = totalInStreams;
|
||||
folder.totalOutputStreams = totalOutStreams;
|
||||
if (totalOutStreams == 0L)
|
||||
throw new IOException("Total output streams can't be 0");
|
||||
long numBindPairs = totalOutStreams - 1L;
|
||||
BindPair[] bindPairs = new BindPair[(int)numBindPairs];
|
||||
for (int j = 0; j < bindPairs.length; j++) {
|
||||
bindPairs[j] = new BindPair();
|
||||
(bindPairs[j]).inIndex = readUint64(header);
|
||||
(bindPairs[j]).outIndex = readUint64(header);
|
||||
}
|
||||
folder.bindPairs = bindPairs;
|
||||
if (totalInStreams < numBindPairs)
|
||||
throw new IOException("Total input streams can't be less than the number of bind pairs");
|
||||
long numPackedStreams = totalInStreams - numBindPairs;
|
||||
long[] packedStreams = new long[(int)numPackedStreams];
|
||||
if (numPackedStreams == 1L) {
|
||||
int k;
|
||||
for (k = 0; k < (int)totalInStreams &&
|
||||
folder.findBindPairForInStream(k) >= 0; k++);
|
||||
if (k == (int)totalInStreams)
|
||||
throw new IOException("Couldn't find stream's bind pair index");
|
||||
packedStreams[0] = (long)k;
|
||||
} else {
|
||||
for (int k = 0; k < (int)numPackedStreams; k++)
|
||||
packedStreams[k] = readUint64(header);
|
||||
}
|
||||
folder.packedStreams = packedStreams;
|
||||
return folder;
|
||||
}
|
||||
|
||||
private BitSet readAllOrBits(DataInput header, int size) throws IOException {
|
||||
BitSet bits;
|
||||
int areAllDefined = header.readUnsignedByte();
|
||||
if (areAllDefined != 0) {
|
||||
bits = new BitSet(size);
|
||||
for (int i = 0; i < size; i++)
|
||||
bits.set(i, true);
|
||||
} else {
|
||||
bits = readBits(header, size);
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
private BitSet readBits(DataInput header, int size) throws IOException {
|
||||
BitSet bits = new BitSet(size);
|
||||
int mask = 0;
|
||||
int cache = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (mask == 0) {
|
||||
mask = 128;
|
||||
cache = header.readUnsignedByte();
|
||||
}
|
||||
bits.set(i, ((cache & mask) != 0));
|
||||
mask >>>= 1;
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
private void readFilesInfo(DataInput header, Archive archive) throws IOException {
|
||||
long numFiles = readUint64(header);
|
||||
SevenZArchiveEntry[] files = new SevenZArchiveEntry[(int)numFiles];
|
||||
for (int i = 0; i < files.length; i++)
|
||||
files[i] = new SevenZArchiveEntry();
|
||||
BitSet isEmptyStream = null;
|
||||
BitSet isEmptyFile = null;
|
||||
BitSet isAnti = null;
|
||||
while (true) {
|
||||
int external;
|
||||
BitSet timesDefined, attributesDefined;
|
||||
byte[] names;
|
||||
int k, nextFile, m, nextName, n;
|
||||
int propertyType = header.readUnsignedByte();
|
||||
if (propertyType == 0)
|
||||
break;
|
||||
long size = readUint64(header);
|
||||
switch (propertyType) {
|
||||
case 14:
|
||||
isEmptyStream = readBits(header, files.length);
|
||||
continue;
|
||||
case 15:
|
||||
if (isEmptyStream == null)
|
||||
throw new IOException("Header format error: kEmptyStream must appear before kEmptyFile");
|
||||
isEmptyFile = readBits(header, isEmptyStream.cardinality());
|
||||
continue;
|
||||
case 16:
|
||||
if (isEmptyStream == null)
|
||||
throw new IOException("Header format error: kEmptyStream must appear before kAnti");
|
||||
isAnti = readBits(header, isEmptyStream.cardinality());
|
||||
continue;
|
||||
case 17:
|
||||
external = header.readUnsignedByte();
|
||||
if (external != 0)
|
||||
throw new IOException("Not implemented");
|
||||
if ((size - 1L & 0x1L) != 0L)
|
||||
throw new IOException("File names length invalid");
|
||||
names = new byte[(int)(size - 1L)];
|
||||
header.readFully(names);
|
||||
nextFile = 0;
|
||||
nextName = 0;
|
||||
for (n = 0; n < names.length; n += 2) {
|
||||
if (names[n] == 0 && names[n + 1] == 0) {
|
||||
files[nextFile++].setName(new String(names, nextName, n - nextName, "UTF-16LE"));
|
||||
nextName = n + 2;
|
||||
}
|
||||
}
|
||||
if (nextName != names.length || nextFile != files.length)
|
||||
throw new IOException("Error parsing file names");
|
||||
continue;
|
||||
case 18:
|
||||
timesDefined = readAllOrBits(header, files.length);
|
||||
k = header.readUnsignedByte();
|
||||
if (k != 0)
|
||||
throw new IOException("Unimplemented");
|
||||
for (m = 0; m < files.length; m++) {
|
||||
files[m].setHasCreationDate(timesDefined.get(m));
|
||||
if (files[m].getHasCreationDate())
|
||||
files[m].setCreationDate(Long.reverseBytes(header.readLong()));
|
||||
}
|
||||
continue;
|
||||
case 19:
|
||||
timesDefined = readAllOrBits(header, files.length);
|
||||
k = header.readUnsignedByte();
|
||||
if (k != 0)
|
||||
throw new IOException("Unimplemented");
|
||||
for (m = 0; m < files.length; m++) {
|
||||
files[m].setHasAccessDate(timesDefined.get(m));
|
||||
if (files[m].getHasAccessDate())
|
||||
files[m].setAccessDate(Long.reverseBytes(header.readLong()));
|
||||
}
|
||||
continue;
|
||||
case 20:
|
||||
timesDefined = readAllOrBits(header, files.length);
|
||||
k = header.readUnsignedByte();
|
||||
if (k != 0)
|
||||
throw new IOException("Unimplemented");
|
||||
for (m = 0; m < files.length; m++) {
|
||||
files[m].setHasLastModifiedDate(timesDefined.get(m));
|
||||
if (files[m].getHasLastModifiedDate())
|
||||
files[m].setLastModifiedDate(Long.reverseBytes(header.readLong()));
|
||||
}
|
||||
continue;
|
||||
case 21:
|
||||
attributesDefined = readAllOrBits(header, files.length);
|
||||
k = header.readUnsignedByte();
|
||||
if (k != 0)
|
||||
throw new IOException("Unimplemented");
|
||||
for (m = 0; m < files.length; m++) {
|
||||
files[m].setHasWindowsAttributes(attributesDefined.get(m));
|
||||
if (files[m].getHasWindowsAttributes())
|
||||
files[m].setWindowsAttributes(Integer.reverseBytes(header.readInt()));
|
||||
}
|
||||
continue;
|
||||
case 24:
|
||||
throw new IOException("kStartPos is unsupported, please report");
|
||||
case 25:
|
||||
throw new IOException("kDummy is unsupported, please report");
|
||||
}
|
||||
throw new IOException("Unknown property " + propertyType);
|
||||
}
|
||||
int nonEmptyFileCounter = 0;
|
||||
int emptyFileCounter = 0;
|
||||
for (int j = 0; j < files.length; j++) {
|
||||
files[j].setHasStream((isEmptyStream == null) ? true : (!isEmptyStream.get(j)));
|
||||
if (files[j].hasStream()) {
|
||||
files[j].setDirectory(false);
|
||||
files[j].setAntiItem(false);
|
||||
files[j].setHasCrc(archive.subStreamsInfo.hasCrc.get(nonEmptyFileCounter));
|
||||
files[j].setCrcValue(archive.subStreamsInfo.crcs[nonEmptyFileCounter]);
|
||||
files[j].setSize(archive.subStreamsInfo.unpackSizes[nonEmptyFileCounter]);
|
||||
nonEmptyFileCounter++;
|
||||
} else {
|
||||
files[j].setDirectory((isEmptyFile == null) ? true : (!isEmptyFile.get(emptyFileCounter)));
|
||||
files[j].setAntiItem((isAnti == null) ? false : isAnti.get(emptyFileCounter));
|
||||
files[j].setHasCrc(false);
|
||||
files[j].setSize(0L);
|
||||
emptyFileCounter++;
|
||||
}
|
||||
}
|
||||
archive.files = files;
|
||||
calculateStreamMap(archive);
|
||||
}
|
||||
|
||||
private void calculateStreamMap(Archive archive) throws IOException {
|
||||
StreamMap streamMap = new StreamMap();
|
||||
int nextFolderPackStreamIndex = 0;
|
||||
int numFolders = (archive.folders != null) ? archive.folders.length : 0;
|
||||
streamMap.folderFirstPackStreamIndex = new int[numFolders];
|
||||
for (int i = 0; i < numFolders; i++) {
|
||||
streamMap.folderFirstPackStreamIndex[i] = nextFolderPackStreamIndex;
|
||||
nextFolderPackStreamIndex += (archive.folders[i]).packedStreams.length;
|
||||
}
|
||||
long nextPackStreamOffset = 0L;
|
||||
int numPackSizes = (archive.packSizes != null) ? archive.packSizes.length : 0;
|
||||
streamMap.packStreamOffsets = new long[numPackSizes];
|
||||
for (int j = 0; j < numPackSizes; j++) {
|
||||
streamMap.packStreamOffsets[j] = nextPackStreamOffset;
|
||||
nextPackStreamOffset += archive.packSizes[j];
|
||||
}
|
||||
streamMap.folderFirstFileIndex = new int[numFolders];
|
||||
streamMap.fileFolderIndex = new int[archive.files.length];
|
||||
int nextFolderIndex = 0;
|
||||
int nextFolderUnpackStreamIndex = 0;
|
||||
for (int k = 0; k < archive.files.length; k++) {
|
||||
if (!archive.files[k].hasStream() && nextFolderUnpackStreamIndex == 0) {
|
||||
streamMap.fileFolderIndex[k] = -1;
|
||||
} else {
|
||||
if (nextFolderUnpackStreamIndex == 0) {
|
||||
for (; nextFolderIndex < archive.folders.length; nextFolderIndex++) {
|
||||
streamMap.folderFirstFileIndex[nextFolderIndex] = k;
|
||||
if ((archive.folders[nextFolderIndex]).numUnpackSubStreams > 0)
|
||||
break;
|
||||
}
|
||||
if (nextFolderIndex >= archive.folders.length)
|
||||
throw new IOException("Too few folders in archive");
|
||||
}
|
||||
streamMap.fileFolderIndex[k] = nextFolderIndex;
|
||||
if (archive.files[k].hasStream()) {
|
||||
nextFolderUnpackStreamIndex++;
|
||||
if (nextFolderUnpackStreamIndex >= (archive.folders[nextFolderIndex]).numUnpackSubStreams) {
|
||||
nextFolderIndex++;
|
||||
nextFolderUnpackStreamIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
archive.streamMap = streamMap;
|
||||
}
|
||||
|
||||
private void buildDecodingStream() throws IOException {
|
||||
int folderIndex = this.archive.streamMap.fileFolderIndex[this.currentEntryIndex];
|
||||
if (folderIndex < 0) {
|
||||
this.currentEntryInputStream = new BoundedInputStream(new ByteArrayInputStream(new byte[0]), 0L);
|
||||
return;
|
||||
}
|
||||
SevenZArchiveEntry file = this.archive.files[this.currentEntryIndex];
|
||||
if (this.currentFolderIndex == folderIndex) {
|
||||
drainPreviousEntry();
|
||||
file.setContentMethods(this.archive.files[this.currentEntryIndex - 1].getContentMethods());
|
||||
} else {
|
||||
this.currentFolderIndex = folderIndex;
|
||||
if (this.currentFolderInputStream != null) {
|
||||
this.currentFolderInputStream.close();
|
||||
this.currentFolderInputStream = null;
|
||||
}
|
||||
Folder folder = this.archive.folders[folderIndex];
|
||||
int firstPackStreamIndex = this.archive.streamMap.folderFirstPackStreamIndex[folderIndex];
|
||||
long folderOffset = 32L + this.archive.packPos + this.archive.streamMap.packStreamOffsets[firstPackStreamIndex];
|
||||
this.currentFolderInputStream = buildDecoderStack(folder, folderOffset, firstPackStreamIndex, file);
|
||||
}
|
||||
InputStream fileStream = new BoundedInputStream(this.currentFolderInputStream, file.getSize());
|
||||
if (file.getHasCrc()) {
|
||||
this.currentEntryInputStream = new CRC32VerifyingInputStream(fileStream, file.getSize(), file.getCrcValue());
|
||||
} else {
|
||||
this.currentEntryInputStream = fileStream;
|
||||
}
|
||||
}
|
||||
|
||||
private void drainPreviousEntry() throws IOException {
|
||||
if (this.currentEntryInputStream != null) {
|
||||
IOUtils.skip(this.currentEntryInputStream, Long.MAX_VALUE);
|
||||
this.currentEntryInputStream.close();
|
||||
this.currentEntryInputStream = null;
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream buildDecoderStack(Folder folder, long folderOffset, int firstPackStreamIndex, SevenZArchiveEntry entry) throws IOException {
|
||||
this.file.seek(folderOffset);
|
||||
InputStream inputStreamStack = new BoundedRandomAccessFileInputStream(this.file, this.archive.packSizes[firstPackStreamIndex]);
|
||||
LinkedList<SevenZMethodConfiguration> methods = new LinkedList<SevenZMethodConfiguration>();
|
||||
for (Coder coder : folder.getOrderedCoders()) {
|
||||
if (coder.numInStreams != 1L || coder.numOutStreams != 1L)
|
||||
throw new IOException("Multi input/output stream coders are not yet supported");
|
||||
SevenZMethod method = SevenZMethod.byId(coder.decompressionMethodId);
|
||||
inputStreamStack = Coders.addDecoder(inputStreamStack, coder, this.password);
|
||||
methods.addFirst(new SevenZMethodConfiguration(method, Coders.findByMethod(method).getOptionsFromCoder(coder, inputStreamStack)));
|
||||
}
|
||||
entry.setContentMethods(methods);
|
||||
if (folder.hasCrc)
|
||||
return new CRC32VerifyingInputStream(inputStreamStack, folder.getUnpackSize(), folder.crc);
|
||||
return inputStreamStack;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
if (this.currentEntryInputStream == null)
|
||||
throw new IllegalStateException("No current 7z entry");
|
||||
return this.currentEntryInputStream.read();
|
||||
}
|
||||
|
||||
public int read(byte[] b) throws IOException {
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
if (this.currentEntryInputStream == null)
|
||||
throw new IllegalStateException("No current 7z entry");
|
||||
return this.currentEntryInputStream.read(b, off, len);
|
||||
}
|
||||
|
||||
private static long readUint64(DataInput in) throws IOException {
|
||||
long firstByte = (long)in.readUnsignedByte();
|
||||
int mask = 128;
|
||||
long value = 0L;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if ((firstByte & (long)mask) == 0L)
|
||||
return value | (firstByte & (long)(mask - 1)) << 8 * i;
|
||||
long nextByte = (long)in.readUnsignedByte();
|
||||
value |= nextByte << 8 * i;
|
||||
mask >>>= 1;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static boolean matches(byte[] signature, int length) {
|
||||
if (length < sevenZSignature.length)
|
||||
return false;
|
||||
for (int i = 0; i < sevenZSignature.length; i++) {
|
||||
if (signature[i] != sevenZSignature[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package org.apache.commons.compress.archivers.sevenz;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public enum SevenZMethod {
|
||||
COPY(new byte[] { (byte)0 }),
|
||||
LZMA(new byte[] { (byte)3, (byte)1, (byte)1 }),
|
||||
LZMA2(new byte[] { (byte)33 }),
|
||||
DEFLATE(new byte[] { (byte)4, (byte)1, (byte)8 }),
|
||||
BZIP2(new byte[] { (byte)4, (byte)2, (byte)2 }),
|
||||
AES256SHA256(new byte[] { (byte)6, (byte)-15, (byte)7, (byte)1 }),
|
||||
BCJ_X86_FILTER(new byte[] { (byte)3, (byte)3, (byte)1, (byte)3 }),
|
||||
BCJ_PPC_FILTER(new byte[] { (byte)3, (byte)3, (byte)2, (byte)5 }),
|
||||
BCJ_IA64_FILTER(new byte[] { (byte)3, (byte)3, (byte)4, (byte)1 }),
|
||||
BCJ_ARM_FILTER(new byte[] { (byte)3, (byte)3, (byte)5, (byte)1 }),
|
||||
BCJ_ARM_THUMB_FILTER(new byte[] { (byte)3, (byte)3, (byte)7, (byte)1 }),
|
||||
BCJ_SPARC_FILTER(new byte[] { (byte)3, (byte)3, (byte)8, (byte)5 }),
|
||||
DELTA_FILTER(new byte[] { (byte)3 });
|
||||
|
||||
private final byte[] id;
|
||||
|
||||
SevenZMethod(byte[] id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
byte[] getId() {
|
||||
byte[] copy = new byte[this.id.length];
|
||||
System.arraycopy(this.id, 0, copy, 0, this.id.length);
|
||||
return copy;
|
||||
}
|
||||
|
||||
static SevenZMethod byId(byte[] id) {
|
||||
for (SevenZMethod m : SevenZMethod.class.getEnumConstants()) {
|
||||
if (Arrays.equals(m.id, id))
|
||||
return m;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package org.apache.commons.compress.archivers.sevenz;
|
||||
|
||||
public class SevenZMethodConfiguration {
|
||||
private final SevenZMethod method;
|
||||
|
||||
private final Object options;
|
||||
|
||||
public SevenZMethodConfiguration(SevenZMethod method) {
|
||||
this(method, null);
|
||||
}
|
||||
|
||||
public SevenZMethodConfiguration(SevenZMethod method, Object options) {
|
||||
this.method = method;
|
||||
this.options = options;
|
||||
if (options != null && !Coders.findByMethod(method).canAcceptOptions(options))
|
||||
throw new IllegalArgumentException("The " + method + " method doesn't support options of type " + options.getClass());
|
||||
}
|
||||
|
||||
public SevenZMethod getMethod() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
public Object getOptions() {
|
||||
return this.options;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,584 @@
|
|||
package org.apache.commons.compress.archivers.sevenz;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.DataOutput;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.CRC32;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.utils.CountingOutputStream;
|
||||
|
||||
public class SevenZOutputFile implements Closeable {
|
||||
private final RandomAccessFile file;
|
||||
|
||||
private final List<SevenZArchiveEntry> files = new ArrayList<SevenZArchiveEntry>();
|
||||
|
||||
private int numNonEmptyStreams = 0;
|
||||
|
||||
private final CRC32 crc32 = new CRC32();
|
||||
|
||||
private final CRC32 compressedCrc32 = new CRC32();
|
||||
|
||||
private long fileBytesWritten = 0L;
|
||||
|
||||
private boolean finished = false;
|
||||
|
||||
private CountingOutputStream currentOutputStream;
|
||||
|
||||
private CountingOutputStream[] additionalCountingStreams;
|
||||
|
||||
private Iterable<? extends SevenZMethodConfiguration> contentMethods = Collections.<SevenZMethodConfiguration>singletonList(new SevenZMethodConfiguration(SevenZMethod.LZMA2));
|
||||
|
||||
private final Map<SevenZArchiveEntry, long[]> additionalSizes = new HashMap<SevenZArchiveEntry, long[]>();
|
||||
|
||||
public SevenZOutputFile(File filename) throws IOException {
|
||||
this.file = new RandomAccessFile(filename, "rw");
|
||||
this.file.seek(32L);
|
||||
}
|
||||
|
||||
public void setContentCompression(SevenZMethod method) {
|
||||
setContentMethods(Collections.<SevenZMethodConfiguration>singletonList(new SevenZMethodConfiguration(method)));
|
||||
}
|
||||
|
||||
public void setContentMethods(Iterable<? extends SevenZMethodConfiguration> methods) {
|
||||
this.contentMethods = reverse(methods);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (!this.finished)
|
||||
finish();
|
||||
this.file.close();
|
||||
}
|
||||
|
||||
public SevenZArchiveEntry createArchiveEntry(File inputFile, String entryName) throws IOException {
|
||||
SevenZArchiveEntry entry = new SevenZArchiveEntry();
|
||||
entry.setDirectory(inputFile.isDirectory());
|
||||
entry.setName(entryName);
|
||||
entry.setLastModifiedDate(new Date(inputFile.lastModified()));
|
||||
return entry;
|
||||
}
|
||||
|
||||
public void putArchiveEntry(ArchiveEntry archiveEntry) throws IOException {
|
||||
SevenZArchiveEntry entry = (SevenZArchiveEntry)archiveEntry;
|
||||
this.files.add(entry);
|
||||
}
|
||||
|
||||
public void closeArchiveEntry() throws IOException {
|
||||
if (this.currentOutputStream != null) {
|
||||
this.currentOutputStream.flush();
|
||||
this.currentOutputStream.close();
|
||||
}
|
||||
SevenZArchiveEntry entry = this.files.get(this.files.size() - 1);
|
||||
if (this.fileBytesWritten > 0L) {
|
||||
entry.setHasStream(true);
|
||||
this.numNonEmptyStreams++;
|
||||
entry.setSize(this.currentOutputStream.getBytesWritten());
|
||||
entry.setCompressedSize(this.fileBytesWritten);
|
||||
entry.setCrcValue(this.crc32.getValue());
|
||||
entry.setCompressedCrcValue(this.compressedCrc32.getValue());
|
||||
entry.setHasCrc(true);
|
||||
if (this.additionalCountingStreams != null) {
|
||||
long[] sizes = new long[this.additionalCountingStreams.length];
|
||||
for (int i = 0; i < this.additionalCountingStreams.length; i++)
|
||||
sizes[i] = this.additionalCountingStreams[i].getBytesWritten();
|
||||
this.additionalSizes.put(entry, sizes);
|
||||
}
|
||||
} else {
|
||||
entry.setHasStream(false);
|
||||
entry.setSize(0L);
|
||||
entry.setCompressedSize(0L);
|
||||
entry.setHasCrc(false);
|
||||
}
|
||||
this.currentOutputStream = null;
|
||||
this.additionalCountingStreams = null;
|
||||
this.crc32.reset();
|
||||
this.compressedCrc32.reset();
|
||||
this.fileBytesWritten = 0L;
|
||||
}
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
getCurrentOutputStream().write(b);
|
||||
}
|
||||
|
||||
public void write(byte[] b) throws IOException {
|
||||
write(b, 0, b.length);
|
||||
}
|
||||
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
if (len > 0)
|
||||
getCurrentOutputStream().write(b, off, len);
|
||||
}
|
||||
|
||||
public void finish() throws IOException {
|
||||
if (this.finished)
|
||||
throw new IOException("This archive has already been finished");
|
||||
this.finished = true;
|
||||
long headerPosition = this.file.getFilePointer();
|
||||
ByteArrayOutputStream headerBaos = new ByteArrayOutputStream();
|
||||
DataOutputStream header = new DataOutputStream(headerBaos);
|
||||
writeHeader(header);
|
||||
header.flush();
|
||||
byte[] headerBytes = headerBaos.toByteArray();
|
||||
this.file.write(headerBytes);
|
||||
CRC32 crc32 = new CRC32();
|
||||
this.file.seek(0L);
|
||||
this.file.write(SevenZFile.sevenZSignature);
|
||||
this.file.write(0);
|
||||
this.file.write(2);
|
||||
ByteArrayOutputStream startHeaderBaos = new ByteArrayOutputStream();
|
||||
DataOutputStream startHeaderStream = new DataOutputStream(startHeaderBaos);
|
||||
startHeaderStream.writeLong(Long.reverseBytes(headerPosition - 32L));
|
||||
startHeaderStream.writeLong(Long.reverseBytes(0xFFFFFFFFL & (long)headerBytes.length));
|
||||
crc32.reset();
|
||||
crc32.update(headerBytes);
|
||||
startHeaderStream.writeInt(Integer.reverseBytes((int)crc32.getValue()));
|
||||
startHeaderStream.flush();
|
||||
byte[] startHeaderBytes = startHeaderBaos.toByteArray();
|
||||
crc32.reset();
|
||||
crc32.update(startHeaderBytes);
|
||||
this.file.writeInt(Integer.reverseBytes((int)crc32.getValue()));
|
||||
this.file.write(startHeaderBytes);
|
||||
}
|
||||
|
||||
private OutputStream getCurrentOutputStream() throws IOException {
|
||||
if (this.currentOutputStream == null)
|
||||
this.currentOutputStream = setupFileOutputStream();
|
||||
return this.currentOutputStream;
|
||||
}
|
||||
|
||||
private CountingOutputStream setupFileOutputStream() throws IOException {
|
||||
if (this.files.isEmpty())
|
||||
throw new IllegalStateException("No current 7z entry");
|
||||
OutputStream out = new OutputStreamWrapper();
|
||||
ArrayList<CountingOutputStream> moreStreams = new ArrayList<CountingOutputStream>();
|
||||
boolean first = true;
|
||||
for (SevenZMethodConfiguration m : getContentMethods(this.files.get(this.files.size() - 1))) {
|
||||
if (!first) {
|
||||
CountingOutputStream cos = new CountingOutputStream(out);
|
||||
moreStreams.add(cos);
|
||||
out = cos;
|
||||
}
|
||||
out = Coders.addEncoder(out, m.getMethod(), m.getOptions());
|
||||
first = false;
|
||||
}
|
||||
if (!moreStreams.isEmpty())
|
||||
this.additionalCountingStreams = moreStreams.<CountingOutputStream>toArray(new CountingOutputStream[moreStreams.size()]);
|
||||
return new CountingOutputStream(out) {
|
||||
public void write(int b) throws IOException {
|
||||
super.write(b);
|
||||
SevenZOutputFile.this.crc32.update(b);
|
||||
}
|
||||
|
||||
public void write(byte[] b) throws IOException {
|
||||
super.write(b);
|
||||
SevenZOutputFile.this.crc32.update(b);
|
||||
}
|
||||
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
super.write(b, off, len);
|
||||
SevenZOutputFile.this.crc32.update(b, off, len);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Iterable<? extends SevenZMethodConfiguration> getContentMethods(SevenZArchiveEntry entry) {
|
||||
Iterable<? extends SevenZMethodConfiguration> ms = entry.getContentMethods();
|
||||
return (ms == null) ? this.contentMethods : ms;
|
||||
}
|
||||
|
||||
private void writeHeader(DataOutput header) throws IOException {
|
||||
header.write(1);
|
||||
header.write(4);
|
||||
writeStreamsInfo(header);
|
||||
writeFilesInfo(header);
|
||||
header.write(0);
|
||||
}
|
||||
|
||||
private void writeStreamsInfo(DataOutput header) throws IOException {
|
||||
if (this.numNonEmptyStreams > 0) {
|
||||
writePackInfo(header);
|
||||
writeUnpackInfo(header);
|
||||
}
|
||||
writeSubStreamsInfo(header);
|
||||
header.write(0);
|
||||
}
|
||||
|
||||
private void writePackInfo(DataOutput header) throws IOException {
|
||||
header.write(6);
|
||||
writeUint64(header, 0L);
|
||||
writeUint64(header, 0xFFFFFFFFL & (long)this.numNonEmptyStreams);
|
||||
header.write(9);
|
||||
for (SevenZArchiveEntry entry : this.files) {
|
||||
if (entry.hasStream())
|
||||
writeUint64(header, entry.getCompressedSize());
|
||||
}
|
||||
header.write(10);
|
||||
header.write(1);
|
||||
for (SevenZArchiveEntry entry : this.files) {
|
||||
if (entry.hasStream())
|
||||
header.writeInt(Integer.reverseBytes((int)entry.getCompressedCrcValue()));
|
||||
}
|
||||
header.write(0);
|
||||
}
|
||||
|
||||
private void writeUnpackInfo(DataOutput header) throws IOException {
|
||||
header.write(7);
|
||||
header.write(11);
|
||||
writeUint64(header, (long)this.numNonEmptyStreams);
|
||||
header.write(0);
|
||||
for (SevenZArchiveEntry entry : this.files) {
|
||||
if (entry.hasStream())
|
||||
writeFolder(header, entry);
|
||||
}
|
||||
header.write(12);
|
||||
for (SevenZArchiveEntry entry : this.files) {
|
||||
if (entry.hasStream()) {
|
||||
long[] moreSizes = this.additionalSizes.get(entry);
|
||||
if (moreSizes != null)
|
||||
for (long s : moreSizes)
|
||||
writeUint64(header, s);
|
||||
writeUint64(header, entry.getSize());
|
||||
}
|
||||
}
|
||||
header.write(10);
|
||||
header.write(1);
|
||||
for (SevenZArchiveEntry entry : this.files) {
|
||||
if (entry.hasStream())
|
||||
header.writeInt(Integer.reverseBytes((int)entry.getCrcValue()));
|
||||
}
|
||||
header.write(0);
|
||||
}
|
||||
|
||||
private void writeFolder(DataOutput header, SevenZArchiveEntry entry) throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
int numCoders = 0;
|
||||
for (SevenZMethodConfiguration m : getContentMethods(entry)) {
|
||||
numCoders++;
|
||||
writeSingleCodec(m, bos);
|
||||
}
|
||||
writeUint64(header, (long)numCoders);
|
||||
header.write(bos.toByteArray());
|
||||
for (int i = 0; i < numCoders - 1; i++) {
|
||||
writeUint64(header, (long)(i + 1));
|
||||
writeUint64(header, (long)i);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeSingleCodec(SevenZMethodConfiguration m, OutputStream bos) throws IOException {
|
||||
byte[] id = m.getMethod().getId();
|
||||
byte[] properties = Coders.findByMethod(m.getMethod()).getOptionsAsProperties(m.getOptions());
|
||||
int codecFlags = id.length;
|
||||
if (properties.length > 0)
|
||||
codecFlags |= 0x20;
|
||||
bos.write(codecFlags);
|
||||
bos.write(id);
|
||||
if (properties.length > 0) {
|
||||
bos.write(properties.length);
|
||||
bos.write(properties);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeSubStreamsInfo(DataOutput header) throws IOException {
|
||||
header.write(8);
|
||||
header.write(0);
|
||||
}
|
||||
|
||||
private void writeFilesInfo(DataOutput header) throws IOException {
|
||||
header.write(5);
|
||||
writeUint64(header, (long)this.files.size());
|
||||
writeFileEmptyStreams(header);
|
||||
writeFileEmptyFiles(header);
|
||||
writeFileAntiItems(header);
|
||||
writeFileNames(header);
|
||||
writeFileCTimes(header);
|
||||
writeFileATimes(header);
|
||||
writeFileMTimes(header);
|
||||
writeFileWindowsAttributes(header);
|
||||
header.write(0);
|
||||
}
|
||||
|
||||
private void writeFileEmptyStreams(DataOutput header) throws IOException {
|
||||
boolean hasEmptyStreams = false;
|
||||
for (SevenZArchiveEntry entry : this.files) {
|
||||
if (!entry.hasStream()) {
|
||||
hasEmptyStreams = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasEmptyStreams) {
|
||||
header.write(14);
|
||||
BitSet emptyStreams = new BitSet(this.files.size());
|
||||
for (int i = 0; i < this.files.size(); i++)
|
||||
emptyStreams.set(i, !this.files.get(i).hasStream());
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream(baos);
|
||||
writeBits(out, emptyStreams, this.files.size());
|
||||
out.flush();
|
||||
byte[] contents = baos.toByteArray();
|
||||
writeUint64(header, (long)contents.length);
|
||||
header.write(contents);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeFileEmptyFiles(DataOutput header) throws IOException {
|
||||
boolean hasEmptyFiles = false;
|
||||
int emptyStreamCounter = 0;
|
||||
BitSet emptyFiles = new BitSet(0);
|
||||
for (int i = 0; i < this.files.size(); i++) {
|
||||
if (!this.files.get(i).hasStream()) {
|
||||
boolean isDir = this.files.get(i).isDirectory();
|
||||
emptyFiles.set(emptyStreamCounter++, !isDir);
|
||||
hasEmptyFiles |= !isDir ? true : false;
|
||||
}
|
||||
}
|
||||
if (hasEmptyFiles) {
|
||||
header.write(15);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream(baos);
|
||||
writeBits(out, emptyFiles, emptyStreamCounter);
|
||||
out.flush();
|
||||
byte[] contents = baos.toByteArray();
|
||||
writeUint64(header, (long)contents.length);
|
||||
header.write(contents);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeFileAntiItems(DataOutput header) throws IOException {
|
||||
boolean hasAntiItems = false;
|
||||
BitSet antiItems = new BitSet(0);
|
||||
int antiItemCounter = 0;
|
||||
for (int i = 0; i < this.files.size(); i++) {
|
||||
if (!this.files.get(i).hasStream()) {
|
||||
boolean isAnti = this.files.get(i).isAntiItem();
|
||||
antiItems.set(antiItemCounter++, isAnti);
|
||||
hasAntiItems |= isAnti;
|
||||
}
|
||||
}
|
||||
if (hasAntiItems) {
|
||||
header.write(16);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream(baos);
|
||||
writeBits(out, antiItems, antiItemCounter);
|
||||
out.flush();
|
||||
byte[] contents = baos.toByteArray();
|
||||
writeUint64(header, (long)contents.length);
|
||||
header.write(contents);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeFileNames(DataOutput header) throws IOException {
|
||||
header.write(17);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream(baos);
|
||||
out.write(0);
|
||||
for (SevenZArchiveEntry entry : this.files) {
|
||||
out.write(entry.getName().getBytes("UTF-16LE"));
|
||||
out.writeShort(0);
|
||||
}
|
||||
out.flush();
|
||||
byte[] contents = baos.toByteArray();
|
||||
writeUint64(header, (long)contents.length);
|
||||
header.write(contents);
|
||||
}
|
||||
|
||||
private void writeFileCTimes(DataOutput header) throws IOException {
|
||||
int numCreationDates = 0;
|
||||
for (SevenZArchiveEntry entry : this.files) {
|
||||
if (entry.getHasCreationDate())
|
||||
numCreationDates++;
|
||||
}
|
||||
if (numCreationDates > 0) {
|
||||
header.write(18);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream(baos);
|
||||
if (numCreationDates != this.files.size()) {
|
||||
out.write(0);
|
||||
BitSet cTimes = new BitSet(this.files.size());
|
||||
for (int i = 0; i < this.files.size(); i++)
|
||||
cTimes.set(i, this.files.get(i).getHasCreationDate());
|
||||
writeBits(out, cTimes, this.files.size());
|
||||
} else {
|
||||
out.write(1);
|
||||
}
|
||||
out.write(0);
|
||||
for (SevenZArchiveEntry entry : this.files) {
|
||||
if (entry.getHasCreationDate())
|
||||
out.writeLong(Long.reverseBytes(SevenZArchiveEntry.javaTimeToNtfsTime(entry.getCreationDate())));
|
||||
}
|
||||
out.flush();
|
||||
byte[] contents = baos.toByteArray();
|
||||
writeUint64(header, (long)contents.length);
|
||||
header.write(contents);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeFileATimes(DataOutput header) throws IOException {
|
||||
int numAccessDates = 0;
|
||||
for (SevenZArchiveEntry entry : this.files) {
|
||||
if (entry.getHasAccessDate())
|
||||
numAccessDates++;
|
||||
}
|
||||
if (numAccessDates > 0) {
|
||||
header.write(19);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream(baos);
|
||||
if (numAccessDates != this.files.size()) {
|
||||
out.write(0);
|
||||
BitSet aTimes = new BitSet(this.files.size());
|
||||
for (int i = 0; i < this.files.size(); i++)
|
||||
aTimes.set(i, this.files.get(i).getHasAccessDate());
|
||||
writeBits(out, aTimes, this.files.size());
|
||||
} else {
|
||||
out.write(1);
|
||||
}
|
||||
out.write(0);
|
||||
for (SevenZArchiveEntry entry : this.files) {
|
||||
if (entry.getHasAccessDate())
|
||||
out.writeLong(Long.reverseBytes(SevenZArchiveEntry.javaTimeToNtfsTime(entry.getAccessDate())));
|
||||
}
|
||||
out.flush();
|
||||
byte[] contents = baos.toByteArray();
|
||||
writeUint64(header, (long)contents.length);
|
||||
header.write(contents);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeFileMTimes(DataOutput header) throws IOException {
|
||||
int numLastModifiedDates = 0;
|
||||
for (SevenZArchiveEntry entry : this.files) {
|
||||
if (entry.getHasLastModifiedDate())
|
||||
numLastModifiedDates++;
|
||||
}
|
||||
if (numLastModifiedDates > 0) {
|
||||
header.write(20);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream(baos);
|
||||
if (numLastModifiedDates != this.files.size()) {
|
||||
out.write(0);
|
||||
BitSet mTimes = new BitSet(this.files.size());
|
||||
for (int i = 0; i < this.files.size(); i++)
|
||||
mTimes.set(i, this.files.get(i).getHasLastModifiedDate());
|
||||
writeBits(out, mTimes, this.files.size());
|
||||
} else {
|
||||
out.write(1);
|
||||
}
|
||||
out.write(0);
|
||||
for (SevenZArchiveEntry entry : this.files) {
|
||||
if (entry.getHasLastModifiedDate())
|
||||
out.writeLong(Long.reverseBytes(SevenZArchiveEntry.javaTimeToNtfsTime(entry.getLastModifiedDate())));
|
||||
}
|
||||
out.flush();
|
||||
byte[] contents = baos.toByteArray();
|
||||
writeUint64(header, (long)contents.length);
|
||||
header.write(contents);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeFileWindowsAttributes(DataOutput header) throws IOException {
|
||||
int numWindowsAttributes = 0;
|
||||
for (SevenZArchiveEntry entry : this.files) {
|
||||
if (entry.getHasWindowsAttributes())
|
||||
numWindowsAttributes++;
|
||||
}
|
||||
if (numWindowsAttributes > 0) {
|
||||
header.write(21);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream(baos);
|
||||
if (numWindowsAttributes != this.files.size()) {
|
||||
out.write(0);
|
||||
BitSet attributes = new BitSet(this.files.size());
|
||||
for (int i = 0; i < this.files.size(); i++)
|
||||
attributes.set(i, this.files.get(i).getHasWindowsAttributes());
|
||||
writeBits(out, attributes, this.files.size());
|
||||
} else {
|
||||
out.write(1);
|
||||
}
|
||||
out.write(0);
|
||||
for (SevenZArchiveEntry entry : this.files) {
|
||||
if (entry.getHasWindowsAttributes())
|
||||
out.writeInt(Integer.reverseBytes(entry.getWindowsAttributes()));
|
||||
}
|
||||
out.flush();
|
||||
byte[] contents = baos.toByteArray();
|
||||
writeUint64(header, (long)contents.length);
|
||||
header.write(contents);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeUint64(DataOutput header, long value) throws IOException {
|
||||
int firstByte = 0;
|
||||
int mask = 128;
|
||||
int i;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (value < 1L << 7 * (i + 1)) {
|
||||
firstByte = (int)((long)firstByte | value >>> 8 * i);
|
||||
break;
|
||||
}
|
||||
firstByte |= mask;
|
||||
mask >>>= 1;
|
||||
}
|
||||
header.write(firstByte);
|
||||
for (; i > 0; i--) {
|
||||
header.write((int)(0xFFL & value));
|
||||
value >>>= 8L;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeBits(DataOutput header, BitSet bits, int length) throws IOException {
|
||||
int cache = 0;
|
||||
int shift = 7;
|
||||
for (int i = 0; i < length; i++) {
|
||||
cache |= (bits.get(i) ? 1 : 0) << shift;
|
||||
if (--shift < 0) {
|
||||
header.write(cache);
|
||||
shift = 7;
|
||||
cache = 0;
|
||||
}
|
||||
}
|
||||
if (shift != 7)
|
||||
header.write(cache);
|
||||
}
|
||||
|
||||
private static <T> Iterable<T> reverse(Iterable<T> i) {
|
||||
LinkedList<T> l = new LinkedList<T>();
|
||||
for (T t : (Iterable<T>)i)
|
||||
l.addFirst(t);
|
||||
return l;
|
||||
}
|
||||
|
||||
private class OutputStreamWrapper extends OutputStream {
|
||||
private OutputStreamWrapper() {}
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
SevenZOutputFile.this.file.write(b);
|
||||
SevenZOutputFile.this.compressedCrc32.update(b);
|
||||
SevenZOutputFile.this.fileBytesWritten++;
|
||||
}
|
||||
|
||||
public void write(byte[] b) throws IOException {
|
||||
write(b, 0, b.length);
|
||||
}
|
||||
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
SevenZOutputFile.this.file.write(b, off, len);
|
||||
SevenZOutputFile.this.compressedCrc32.update(b, off, len);
|
||||
SevenZOutputFile.this.fileBytesWritten += (long)len;
|
||||
}
|
||||
|
||||
public void flush() throws IOException {}
|
||||
|
||||
public void close() throws IOException {}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package org.apache.commons.compress.archivers.sevenz;
|
||||
|
||||
class StartHeader {
|
||||
long nextHeaderOffset;
|
||||
|
||||
long nextHeaderSize;
|
||||
|
||||
long nextHeaderCrc;
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package org.apache.commons.compress.archivers.sevenz;
|
||||
|
||||
class StreamMap {
|
||||
int[] folderFirstPackStreamIndex;
|
||||
|
||||
long[] packStreamOffsets;
|
||||
|
||||
int[] folderFirstFileIndex;
|
||||
|
||||
int[] fileFolderIndex;
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package org.apache.commons.compress.archivers.sevenz;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
class SubStreamsInfo {
|
||||
long[] unpackSizes;
|
||||
|
||||
BitSet hasCrc;
|
||||
|
||||
long[] crcs;
|
||||
}
|
||||
|
|
@ -0,0 +1,484 @@
|
|||
package org.apache.commons.compress.archivers.tar;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipEncoding;
|
||||
import org.apache.commons.compress.utils.ArchiveUtils;
|
||||
|
||||
public class TarArchiveEntry implements TarConstants, ArchiveEntry {
|
||||
private String name = "";
|
||||
|
||||
private int mode;
|
||||
|
||||
private int userId = 0;
|
||||
|
||||
private int groupId = 0;
|
||||
|
||||
private long size = 0L;
|
||||
|
||||
private long modTime;
|
||||
|
||||
private boolean checkSumOK;
|
||||
|
||||
private byte linkFlag;
|
||||
|
||||
private String linkName = "";
|
||||
|
||||
private String magic = "ustar\000";
|
||||
|
||||
private String version = "00";
|
||||
|
||||
private String userName;
|
||||
|
||||
private String groupName = "";
|
||||
|
||||
private int devMajor = 0;
|
||||
|
||||
private int devMinor = 0;
|
||||
|
||||
private boolean isExtended;
|
||||
|
||||
private long realSize;
|
||||
|
||||
private final File file;
|
||||
|
||||
public static final int MAX_NAMELEN = 31;
|
||||
|
||||
public static final int DEFAULT_DIR_MODE = 16877;
|
||||
|
||||
public static final int DEFAULT_FILE_MODE = 33188;
|
||||
|
||||
public static final int MILLIS_PER_SECOND = 1000;
|
||||
|
||||
private TarArchiveEntry() {
|
||||
String user = System.getProperty("user.name", "");
|
||||
if (user.length() > 31)
|
||||
user = user.substring(0, 31);
|
||||
this.userName = user;
|
||||
this.file = null;
|
||||
}
|
||||
|
||||
public TarArchiveEntry(String name) {
|
||||
this(name, false);
|
||||
}
|
||||
|
||||
public TarArchiveEntry(String name, boolean preserveLeadingSlashes) {
|
||||
this();
|
||||
name = normalizeFileName(name, preserveLeadingSlashes);
|
||||
boolean isDir = name.endsWith("/");
|
||||
this.name = name;
|
||||
this.mode = isDir ? 16877 : 33188;
|
||||
this.linkFlag = isDir ? 53 : 48;
|
||||
this.modTime = new Date().getTime() / 1000L;
|
||||
this.userName = "";
|
||||
}
|
||||
|
||||
public TarArchiveEntry(String name, byte linkFlag) {
|
||||
this(name, linkFlag, false);
|
||||
}
|
||||
|
||||
public TarArchiveEntry(String name, byte linkFlag, boolean preserveLeadingSlashes) {
|
||||
this(name, preserveLeadingSlashes);
|
||||
this.linkFlag = linkFlag;
|
||||
if (linkFlag == 76) {
|
||||
this.magic = "ustar ";
|
||||
this.version = " \000";
|
||||
}
|
||||
}
|
||||
|
||||
public TarArchiveEntry(File file) {
|
||||
this(file, normalizeFileName(file.getPath(), false));
|
||||
}
|
||||
|
||||
public TarArchiveEntry(File file, String fileName) {
|
||||
this.file = file;
|
||||
if (file.isDirectory()) {
|
||||
this.mode = 16877;
|
||||
this.linkFlag = 53;
|
||||
int nameLength = fileName.length();
|
||||
if (nameLength == 0 || fileName.charAt(nameLength - 1) != '/') {
|
||||
this.name = fileName + "/";
|
||||
} else {
|
||||
this.name = fileName;
|
||||
}
|
||||
} else {
|
||||
this.mode = 33188;
|
||||
this.linkFlag = 48;
|
||||
this.size = file.length();
|
||||
this.name = fileName;
|
||||
}
|
||||
this.modTime = file.lastModified() / 1000L;
|
||||
this.userName = "";
|
||||
}
|
||||
|
||||
public TarArchiveEntry(byte[] headerBuf) {
|
||||
this();
|
||||
parseTarHeader(headerBuf);
|
||||
}
|
||||
|
||||
public TarArchiveEntry(byte[] headerBuf, ZipEncoding encoding) throws IOException {
|
||||
this();
|
||||
parseTarHeader(headerBuf, encoding);
|
||||
}
|
||||
|
||||
public boolean equals(TarArchiveEntry it) {
|
||||
return getName().equals(it.getName());
|
||||
}
|
||||
|
||||
public boolean equals(Object it) {
|
||||
if (it == null || getClass() != it.getClass())
|
||||
return false;
|
||||
return equals((TarArchiveEntry)it);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return getName().hashCode();
|
||||
}
|
||||
|
||||
public boolean isDescendent(TarArchiveEntry desc) {
|
||||
return desc.getName().startsWith(getName());
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name.toString();
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = normalizeFileName(name, false);
|
||||
}
|
||||
|
||||
public void setMode(int mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public String getLinkName() {
|
||||
return this.linkName.toString();
|
||||
}
|
||||
|
||||
public void setLinkName(String link) {
|
||||
this.linkName = link;
|
||||
}
|
||||
|
||||
public int getUserId() {
|
||||
return this.userId;
|
||||
}
|
||||
|
||||
public void setUserId(int userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public int getGroupId() {
|
||||
return this.groupId;
|
||||
}
|
||||
|
||||
public void setGroupId(int groupId) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
return this.userName.toString();
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public String getGroupName() {
|
||||
return this.groupName.toString();
|
||||
}
|
||||
|
||||
public void setGroupName(String groupName) {
|
||||
this.groupName = groupName;
|
||||
}
|
||||
|
||||
public void setIds(int userId, int groupId) {
|
||||
setUserId(userId);
|
||||
setGroupId(groupId);
|
||||
}
|
||||
|
||||
public void setNames(String userName, String groupName) {
|
||||
setUserName(userName);
|
||||
setGroupName(groupName);
|
||||
}
|
||||
|
||||
public void setModTime(long time) {
|
||||
this.modTime = time / 1000L;
|
||||
}
|
||||
|
||||
public void setModTime(Date time) {
|
||||
this.modTime = time.getTime() / 1000L;
|
||||
}
|
||||
|
||||
public Date getModTime() {
|
||||
return new Date(this.modTime * 1000L);
|
||||
}
|
||||
|
||||
public Date getLastModifiedDate() {
|
||||
return getModTime();
|
||||
}
|
||||
|
||||
public boolean isCheckSumOK() {
|
||||
return this.checkSumOK;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return this.file;
|
||||
}
|
||||
|
||||
public int getMode() {
|
||||
return this.mode;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public void setSize(long size) {
|
||||
if (size < 0L)
|
||||
throw new IllegalArgumentException("Size is out of range: " + size);
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public int getDevMajor() {
|
||||
return this.devMajor;
|
||||
}
|
||||
|
||||
public void setDevMajor(int devNo) {
|
||||
if (devNo < 0)
|
||||
throw new IllegalArgumentException("Major device number is out of range: " + devNo);
|
||||
this.devMajor = devNo;
|
||||
}
|
||||
|
||||
public int getDevMinor() {
|
||||
return this.devMinor;
|
||||
}
|
||||
|
||||
public void setDevMinor(int devNo) {
|
||||
if (devNo < 0)
|
||||
throw new IllegalArgumentException("Minor device number is out of range: " + devNo);
|
||||
this.devMinor = devNo;
|
||||
}
|
||||
|
||||
public boolean isExtended() {
|
||||
return this.isExtended;
|
||||
}
|
||||
|
||||
public long getRealSize() {
|
||||
return this.realSize;
|
||||
}
|
||||
|
||||
public boolean isGNUSparse() {
|
||||
return (this.linkFlag == 83);
|
||||
}
|
||||
|
||||
public boolean isGNULongLinkEntry() {
|
||||
return (this.linkFlag == 75 && this.name.equals("././@LongLink"));
|
||||
}
|
||||
|
||||
public boolean isGNULongNameEntry() {
|
||||
return (this.linkFlag == 76 && this.name.equals("././@LongLink"));
|
||||
}
|
||||
|
||||
public boolean isPaxHeader() {
|
||||
return (this.linkFlag == 120 || this.linkFlag == 88);
|
||||
}
|
||||
|
||||
public boolean isGlobalPaxHeader() {
|
||||
return (this.linkFlag == 103);
|
||||
}
|
||||
|
||||
public boolean isDirectory() {
|
||||
if (this.file != null)
|
||||
return this.file.isDirectory();
|
||||
if (this.linkFlag == 53)
|
||||
return true;
|
||||
if (getName().endsWith("/"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isFile() {
|
||||
if (this.file != null)
|
||||
return this.file.isFile();
|
||||
if (this.linkFlag == 0 || this.linkFlag == 48)
|
||||
return true;
|
||||
return !getName().endsWith("/");
|
||||
}
|
||||
|
||||
public boolean isSymbolicLink() {
|
||||
return (this.linkFlag == 50);
|
||||
}
|
||||
|
||||
public boolean isLink() {
|
||||
return (this.linkFlag == 49);
|
||||
}
|
||||
|
||||
public boolean isCharacterDevice() {
|
||||
return (this.linkFlag == 51);
|
||||
}
|
||||
|
||||
public boolean isBlockDevice() {
|
||||
return (this.linkFlag == 52);
|
||||
}
|
||||
|
||||
public boolean isFIFO() {
|
||||
return (this.linkFlag == 54);
|
||||
}
|
||||
|
||||
public TarArchiveEntry[] getDirectoryEntries() {
|
||||
if (this.file == null || !this.file.isDirectory())
|
||||
return new TarArchiveEntry[0];
|
||||
String[] list = this.file.list();
|
||||
TarArchiveEntry[] result = new TarArchiveEntry[list.length];
|
||||
for (int i = 0; i < list.length; i++)
|
||||
result[i] = new TarArchiveEntry(new File(this.file, list[i]));
|
||||
return result;
|
||||
}
|
||||
|
||||
public void writeEntryHeader(byte[] outbuf) {
|
||||
try {
|
||||
writeEntryHeader(outbuf, TarUtils.DEFAULT_ENCODING, false);
|
||||
} catch (IOException ex) {
|
||||
try {
|
||||
writeEntryHeader(outbuf, TarUtils.FALLBACK_ENCODING, false);
|
||||
} catch (IOException ex2) {
|
||||
throw new RuntimeException(ex2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void writeEntryHeader(byte[] outbuf, ZipEncoding encoding, boolean starMode) throws IOException {
|
||||
int offset = 0;
|
||||
offset = TarUtils.formatNameBytes(this.name, outbuf, offset, 100, encoding);
|
||||
offset = writeEntryHeaderField((long)this.mode, outbuf, offset, 8, starMode);
|
||||
offset = writeEntryHeaderField((long)this.userId, outbuf, offset, 8, starMode);
|
||||
offset = writeEntryHeaderField((long)this.groupId, outbuf, offset, 8, starMode);
|
||||
offset = writeEntryHeaderField(this.size, outbuf, offset, 12, starMode);
|
||||
offset = writeEntryHeaderField(this.modTime, outbuf, offset, 12, starMode);
|
||||
int csOffset = offset;
|
||||
for (int c = 0; c < 8; c++)
|
||||
outbuf[offset++] = 32;
|
||||
outbuf[offset++] = this.linkFlag;
|
||||
offset = TarUtils.formatNameBytes(this.linkName, outbuf, offset, 100, encoding);
|
||||
offset = TarUtils.formatNameBytes(this.magic, outbuf, offset, 6);
|
||||
offset = TarUtils.formatNameBytes(this.version, outbuf, offset, 2);
|
||||
offset = TarUtils.formatNameBytes(this.userName, outbuf, offset, 32, encoding);
|
||||
offset = TarUtils.formatNameBytes(this.groupName, outbuf, offset, 32, encoding);
|
||||
offset = writeEntryHeaderField((long)this.devMajor, outbuf, offset, 8, starMode);
|
||||
offset = writeEntryHeaderField((long)this.devMinor, outbuf, offset, 8, starMode);
|
||||
while (offset < outbuf.length)
|
||||
outbuf[offset++] = 0;
|
||||
long chk = TarUtils.computeCheckSum(outbuf);
|
||||
TarUtils.formatCheckSumOctalBytes(chk, outbuf, csOffset, 8);
|
||||
}
|
||||
|
||||
private int writeEntryHeaderField(long value, byte[] outbuf, int offset, int length, boolean starMode) {
|
||||
if (!starMode && (value < 0L || value >= 1L << 3 * (length - 1)))
|
||||
return TarUtils.formatLongOctalBytes(0L, outbuf, offset, length);
|
||||
return TarUtils.formatLongOctalOrBinaryBytes(value, outbuf, offset, length);
|
||||
}
|
||||
|
||||
public void parseTarHeader(byte[] header) {
|
||||
try {
|
||||
parseTarHeader(header, TarUtils.DEFAULT_ENCODING);
|
||||
} catch (IOException ex) {
|
||||
try {
|
||||
parseTarHeader(header, TarUtils.DEFAULT_ENCODING, true);
|
||||
} catch (IOException ex2) {
|
||||
throw new RuntimeException(ex2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void parseTarHeader(byte[] header, ZipEncoding encoding) throws IOException {
|
||||
parseTarHeader(header, encoding, false);
|
||||
}
|
||||
|
||||
private void parseTarHeader(byte[] header, ZipEncoding encoding, boolean oldStyle) throws IOException {
|
||||
String prefix;
|
||||
int offset = 0;
|
||||
this.name = oldStyle ? TarUtils.parseName(header, offset, 100) : TarUtils.parseName(header, offset, 100, encoding);
|
||||
offset += 100;
|
||||
this.mode = (int)TarUtils.parseOctalOrBinary(header, offset, 8);
|
||||
offset += 8;
|
||||
this.userId = (int)TarUtils.parseOctalOrBinary(header, offset, 8);
|
||||
offset += 8;
|
||||
this.groupId = (int)TarUtils.parseOctalOrBinary(header, offset, 8);
|
||||
offset += 8;
|
||||
this.size = TarUtils.parseOctalOrBinary(header, offset, 12);
|
||||
offset += 12;
|
||||
this.modTime = TarUtils.parseOctalOrBinary(header, offset, 12);
|
||||
offset += 12;
|
||||
this.checkSumOK = TarUtils.verifyCheckSum(header);
|
||||
offset += 8;
|
||||
this.linkFlag = header[offset++];
|
||||
this.linkName = oldStyle ? TarUtils.parseName(header, offset, 100) : TarUtils.parseName(header, offset, 100, encoding);
|
||||
offset += 100;
|
||||
this.magic = TarUtils.parseName(header, offset, 6);
|
||||
offset += 6;
|
||||
this.version = TarUtils.parseName(header, offset, 2);
|
||||
offset += 2;
|
||||
this.userName = oldStyle ? TarUtils.parseName(header, offset, 32) : TarUtils.parseName(header, offset, 32, encoding);
|
||||
offset += 32;
|
||||
this.groupName = oldStyle ? TarUtils.parseName(header, offset, 32) : TarUtils.parseName(header, offset, 32, encoding);
|
||||
offset += 32;
|
||||
this.devMajor = (int)TarUtils.parseOctalOrBinary(header, offset, 8);
|
||||
offset += 8;
|
||||
this.devMinor = (int)TarUtils.parseOctalOrBinary(header, offset, 8);
|
||||
offset += 8;
|
||||
int type = evaluateType(header);
|
||||
switch (type) {
|
||||
case 2:
|
||||
offset += 12;
|
||||
offset += 12;
|
||||
offset += 12;
|
||||
offset += 4;
|
||||
offset++;
|
||||
offset += 96;
|
||||
this.isExtended = TarUtils.parseBoolean(header, offset);
|
||||
offset++;
|
||||
this.realSize = TarUtils.parseOctal(header, offset, 12);
|
||||
offset += 12;
|
||||
break;
|
||||
default:
|
||||
prefix = oldStyle ? TarUtils.parseName(header, offset, 155) : TarUtils.parseName(header, offset, 155, encoding);
|
||||
if (isDirectory() && !this.name.endsWith("/"))
|
||||
this.name += "/";
|
||||
if (prefix.length() > 0)
|
||||
this.name = prefix + "/" + this.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static String normalizeFileName(String fileName, boolean preserveLeadingSlashes) {
|
||||
String osname = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
|
||||
if (osname != null)
|
||||
if (osname.startsWith("windows")) {
|
||||
if (fileName.length() > 2) {
|
||||
char ch1 = fileName.charAt(0);
|
||||
char ch2 = fileName.charAt(1);
|
||||
if (ch2 == ':' && ((ch1 >= 'a' && ch1 <= 'z') || (ch1 >= 'A' && ch1 <= 'Z')))
|
||||
fileName = fileName.substring(2);
|
||||
}
|
||||
} else if (osname.indexOf("netware") > -1) {
|
||||
int colon = fileName.indexOf(':');
|
||||
if (colon != -1)
|
||||
fileName = fileName.substring(colon + 1);
|
||||
}
|
||||
fileName = fileName.replace(File.separatorChar, '/');
|
||||
while (!preserveLeadingSlashes && fileName.startsWith("/"))
|
||||
fileName = fileName.substring(1);
|
||||
return fileName;
|
||||
}
|
||||
|
||||
private int evaluateType(byte[] header) {
|
||||
if (ArchiveUtils.matchAsciiBuffer("ustar ", header, 257, 6))
|
||||
return 2;
|
||||
if (ArchiveUtils.matchAsciiBuffer("ustar\000", header, 257, 6))
|
||||
return 3;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,363 @@
|
|||
package org.apache.commons.compress.archivers.tar;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.ArchiveInputStream;
|
||||
import org.apache.commons.compress.archivers.zip.ZipEncoding;
|
||||
import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
|
||||
import org.apache.commons.compress.utils.ArchiveUtils;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
|
||||
public class TarArchiveInputStream extends ArchiveInputStream {
|
||||
private static final int SMALL_BUFFER_SIZE = 256;
|
||||
|
||||
private final byte[] SMALL_BUF = new byte[256];
|
||||
|
||||
private final int recordSize;
|
||||
|
||||
private final int blockSize;
|
||||
|
||||
private boolean hasHitEOF;
|
||||
|
||||
private long entrySize;
|
||||
|
||||
private long entryOffset;
|
||||
|
||||
private final InputStream is;
|
||||
|
||||
private TarArchiveEntry currEntry;
|
||||
|
||||
private final ZipEncoding encoding;
|
||||
|
||||
public TarArchiveInputStream(InputStream is) {
|
||||
this(is, 10240, 512);
|
||||
}
|
||||
|
||||
public TarArchiveInputStream(InputStream is, String encoding) {
|
||||
this(is, 10240, 512, encoding);
|
||||
}
|
||||
|
||||
public TarArchiveInputStream(InputStream is, int blockSize) {
|
||||
this(is, blockSize, 512);
|
||||
}
|
||||
|
||||
public TarArchiveInputStream(InputStream is, int blockSize, String encoding) {
|
||||
this(is, blockSize, 512, encoding);
|
||||
}
|
||||
|
||||
public TarArchiveInputStream(InputStream is, int blockSize, int recordSize) {
|
||||
this(is, blockSize, recordSize, null);
|
||||
}
|
||||
|
||||
public TarArchiveInputStream(InputStream is, int blockSize, int recordSize, String encoding) {
|
||||
this.is = is;
|
||||
this.hasHitEOF = false;
|
||||
this.encoding = ZipEncodingHelper.getZipEncoding(encoding);
|
||||
this.recordSize = recordSize;
|
||||
this.blockSize = blockSize;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
this.is.close();
|
||||
}
|
||||
|
||||
public int getRecordSize() {
|
||||
return this.recordSize;
|
||||
}
|
||||
|
||||
public int available() throws IOException {
|
||||
if (this.entrySize - this.entryOffset > Integer.MAX_VALUE)
|
||||
return Integer.MAX_VALUE;
|
||||
return (int)(this.entrySize - this.entryOffset);
|
||||
}
|
||||
|
||||
public long skip(long n) throws IOException {
|
||||
if (n <= 0L)
|
||||
return 0L;
|
||||
long available = this.entrySize - this.entryOffset;
|
||||
long skipped = this.is.skip(Math.min(n, available));
|
||||
count(skipped);
|
||||
this.entryOffset += skipped;
|
||||
return skipped;
|
||||
}
|
||||
|
||||
public synchronized void reset() {}
|
||||
|
||||
public TarArchiveEntry getNextTarEntry() throws IOException {
|
||||
if (this.hasHitEOF)
|
||||
return null;
|
||||
if (this.currEntry != null) {
|
||||
IOUtils.skip(this, Long.MAX_VALUE);
|
||||
skipRecordPadding();
|
||||
}
|
||||
byte[] headerBuf = getRecord();
|
||||
if (headerBuf == null) {
|
||||
this.currEntry = null;
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
this.currEntry = new TarArchiveEntry(headerBuf, this.encoding);
|
||||
} catch (IllegalArgumentException e) {
|
||||
IOException ioe = new IOException("Error detected parsing the header");
|
||||
ioe.initCause(e);
|
||||
throw ioe;
|
||||
}
|
||||
this.entryOffset = 0L;
|
||||
this.entrySize = this.currEntry.getSize();
|
||||
if (this.currEntry.isGNULongLinkEntry()) {
|
||||
byte[] longLinkData = getLongNameData();
|
||||
if (longLinkData == null)
|
||||
return null;
|
||||
this.currEntry.setLinkName(this.encoding.decode(longLinkData));
|
||||
}
|
||||
if (this.currEntry.isGNULongNameEntry()) {
|
||||
byte[] longNameData = getLongNameData();
|
||||
if (longNameData == null)
|
||||
return null;
|
||||
this.currEntry.setName(this.encoding.decode(longNameData));
|
||||
}
|
||||
if (this.currEntry.isPaxHeader())
|
||||
paxHeaders();
|
||||
if (this.currEntry.isGNUSparse())
|
||||
readGNUSparse();
|
||||
this.entrySize = this.currEntry.getSize();
|
||||
return this.currEntry;
|
||||
}
|
||||
|
||||
private void skipRecordPadding() throws IOException {
|
||||
if (this.entrySize > 0L && this.entrySize % (long)this.recordSize != 0L) {
|
||||
long numRecords = this.entrySize / (long)this.recordSize + 1L;
|
||||
long padding = numRecords * (long)this.recordSize - this.entrySize;
|
||||
long skipped = IOUtils.skip(this.is, padding);
|
||||
count(skipped);
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] getLongNameData() throws IOException {
|
||||
ByteArrayOutputStream longName = new ByteArrayOutputStream();
|
||||
int length = 0;
|
||||
while ((length = read(this.SMALL_BUF)) >= 0)
|
||||
longName.write(this.SMALL_BUF, 0, length);
|
||||
getNextEntry();
|
||||
if (this.currEntry == null)
|
||||
return null;
|
||||
byte[] longNameData = longName.toByteArray();
|
||||
length = longNameData.length;
|
||||
while (length > 0 && longNameData[length - 1] == 0)
|
||||
length--;
|
||||
if (length != longNameData.length) {
|
||||
byte[] l = new byte[length];
|
||||
System.arraycopy(longNameData, 0, l, 0, length);
|
||||
longNameData = l;
|
||||
}
|
||||
return longNameData;
|
||||
}
|
||||
|
||||
private byte[] getRecord() throws IOException {
|
||||
byte[] headerBuf = readRecord();
|
||||
this.hasHitEOF = isEOFRecord(headerBuf);
|
||||
if (this.hasHitEOF && headerBuf != null) {
|
||||
tryToConsumeSecondEOFRecord();
|
||||
consumeRemainderOfLastBlock();
|
||||
headerBuf = null;
|
||||
}
|
||||
return headerBuf;
|
||||
}
|
||||
|
||||
protected boolean isEOFRecord(byte[] record) {
|
||||
return (record == null || ArchiveUtils.isArrayZero(record, this.recordSize));
|
||||
}
|
||||
|
||||
protected byte[] readRecord() throws IOException {
|
||||
byte[] record = new byte[this.recordSize];
|
||||
int readNow = IOUtils.readFully(this.is, record);
|
||||
count(readNow);
|
||||
if (readNow != this.recordSize)
|
||||
return null;
|
||||
return record;
|
||||
}
|
||||
|
||||
private void paxHeaders() throws IOException {
|
||||
Map<String, String> headers = parsePaxHeaders(this);
|
||||
getNextEntry();
|
||||
applyPaxHeadersToCurrentEntry(headers);
|
||||
}
|
||||
|
||||
Map<String, String> parsePaxHeaders(InputStream i) throws IOException {
|
||||
int ch;
|
||||
Map<String, String> headers = new HashMap<String, String>();
|
||||
do {
|
||||
int len = 0;
|
||||
int read = 0;
|
||||
while ((ch = i.read()) != -1) {
|
||||
read++;
|
||||
if (ch == 32) {
|
||||
ByteArrayOutputStream coll = new ByteArrayOutputStream();
|
||||
while ((ch = i.read()) != -1) {
|
||||
read++;
|
||||
if (ch == 61) {
|
||||
String keyword = coll.toString("UTF-8");
|
||||
byte[] rest = new byte[len - read];
|
||||
int got = IOUtils.readFully(i, rest);
|
||||
if (got != len - read)
|
||||
throw new IOException("Failed to read Paxheader. Expected " + (len - read) + " bytes, read " + got);
|
||||
String value = new String(rest, 0, len - read - 1, "UTF-8");
|
||||
headers.put(keyword, value);
|
||||
break;
|
||||
}
|
||||
coll.write((byte)ch);
|
||||
}
|
||||
break;
|
||||
}
|
||||
len *= 10;
|
||||
len += ch - 48;
|
||||
}
|
||||
} while (ch != -1);
|
||||
return headers;
|
||||
}
|
||||
|
||||
private void applyPaxHeadersToCurrentEntry(Map<String, String> headers) {
|
||||
for (Map.Entry<String, String> ent : headers.entrySet()) {
|
||||
String key = ent.getKey();
|
||||
String val = ent.getValue();
|
||||
if ("path".equals(key)) {
|
||||
this.currEntry.setName(val);
|
||||
continue;
|
||||
}
|
||||
if ("linkpath".equals(key)) {
|
||||
this.currEntry.setLinkName(val);
|
||||
continue;
|
||||
}
|
||||
if ("gid".equals(key)) {
|
||||
this.currEntry.setGroupId(Integer.parseInt(val));
|
||||
continue;
|
||||
}
|
||||
if ("gname".equals(key)) {
|
||||
this.currEntry.setGroupName(val);
|
||||
continue;
|
||||
}
|
||||
if ("uid".equals(key)) {
|
||||
this.currEntry.setUserId(Integer.parseInt(val));
|
||||
continue;
|
||||
}
|
||||
if ("uname".equals(key)) {
|
||||
this.currEntry.setUserName(val);
|
||||
continue;
|
||||
}
|
||||
if ("size".equals(key)) {
|
||||
this.currEntry.setSize(Long.parseLong(val));
|
||||
continue;
|
||||
}
|
||||
if ("mtime".equals(key)) {
|
||||
this.currEntry.setModTime((long)(Double.parseDouble(val) * 1000.0D));
|
||||
continue;
|
||||
}
|
||||
if ("SCHILY.devminor".equals(key)) {
|
||||
this.currEntry.setDevMinor(Integer.parseInt(val));
|
||||
continue;
|
||||
}
|
||||
if ("SCHILY.devmajor".equals(key))
|
||||
this.currEntry.setDevMajor(Integer.parseInt(val));
|
||||
}
|
||||
}
|
||||
|
||||
private void readGNUSparse() throws IOException {
|
||||
if (this.currEntry.isExtended()) {
|
||||
TarArchiveSparseEntry entry;
|
||||
do {
|
||||
byte[] headerBuf = getRecord();
|
||||
if (headerBuf == null) {
|
||||
this.currEntry = null;
|
||||
break;
|
||||
}
|
||||
entry = new TarArchiveSparseEntry(headerBuf);
|
||||
} while (entry.isExtended());
|
||||
}
|
||||
}
|
||||
|
||||
public ArchiveEntry getNextEntry() throws IOException {
|
||||
return getNextTarEntry();
|
||||
}
|
||||
|
||||
private void tryToConsumeSecondEOFRecord() throws IOException {
|
||||
boolean shouldReset = true;
|
||||
boolean marked = this.is.markSupported();
|
||||
if (marked)
|
||||
this.is.mark(this.recordSize);
|
||||
try {
|
||||
shouldReset = !isEOFRecord(readRecord());
|
||||
} finally {
|
||||
if (shouldReset && marked) {
|
||||
pushedBackBytes((long)this.recordSize);
|
||||
this.is.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int read(byte[] buf, int offset, int numToRead) throws IOException {
|
||||
int totalRead = 0;
|
||||
if (this.hasHitEOF || this.entryOffset >= this.entrySize)
|
||||
return -1;
|
||||
if (this.currEntry == null)
|
||||
throw new IllegalStateException("No current tar entry");
|
||||
numToRead = Math.min(numToRead, available());
|
||||
totalRead = this.is.read(buf, offset, numToRead);
|
||||
if (totalRead == -1) {
|
||||
if (numToRead > 0)
|
||||
throw new IOException("Truncated TAR archive");
|
||||
this.hasHitEOF = true;
|
||||
} else {
|
||||
count(totalRead);
|
||||
this.entryOffset += (long)totalRead;
|
||||
}
|
||||
return totalRead;
|
||||
}
|
||||
|
||||
public boolean canReadEntryData(ArchiveEntry ae) {
|
||||
if (ae instanceof TarArchiveEntry) {
|
||||
TarArchiveEntry te = (TarArchiveEntry)ae;
|
||||
return !te.isGNUSparse();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public TarArchiveEntry getCurrentEntry() {
|
||||
return this.currEntry;
|
||||
}
|
||||
|
||||
protected final void setCurrentEntry(TarArchiveEntry e) {
|
||||
this.currEntry = e;
|
||||
}
|
||||
|
||||
protected final boolean isAtEOF() {
|
||||
return this.hasHitEOF;
|
||||
}
|
||||
|
||||
protected final void setAtEOF(boolean b) {
|
||||
this.hasHitEOF = b;
|
||||
}
|
||||
|
||||
private void consumeRemainderOfLastBlock() throws IOException {
|
||||
long bytesReadOfLastBlock = getBytesRead() % (long)this.blockSize;
|
||||
if (bytesReadOfLastBlock > 0L) {
|
||||
long skipped = IOUtils.skip(this.is, (long)this.blockSize - bytesReadOfLastBlock);
|
||||
count(skipped);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean matches(byte[] signature, int length) {
|
||||
if (length < 265)
|
||||
return false;
|
||||
if (ArchiveUtils.matchAsciiBuffer("ustar\000", signature, 257, 6) && ArchiveUtils.matchAsciiBuffer("00", signature, 263, 2))
|
||||
return true;
|
||||
if (ArchiveUtils.matchAsciiBuffer("ustar ", signature, 257, 6) && (ArchiveUtils.matchAsciiBuffer(" \000", signature, 263, 2) || ArchiveUtils.matchAsciiBuffer("0\000", signature, 263, 2)))
|
||||
return true;
|
||||
if (ArchiveUtils.matchAsciiBuffer("ustar\000", signature, 257, 6) && ArchiveUtils.matchAsciiBuffer("\000\000", signature, 263, 2))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,359 @@
|
|||
package org.apache.commons.compress.archivers.tar;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.ArchiveOutputStream;
|
||||
import org.apache.commons.compress.archivers.zip.ZipEncoding;
|
||||
import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
|
||||
import org.apache.commons.compress.utils.CountingOutputStream;
|
||||
|
||||
public class TarArchiveOutputStream extends ArchiveOutputStream {
|
||||
public static final int LONGFILE_ERROR = 0;
|
||||
|
||||
public static final int LONGFILE_TRUNCATE = 1;
|
||||
|
||||
public static final int LONGFILE_GNU = 2;
|
||||
|
||||
public static final int LONGFILE_POSIX = 3;
|
||||
|
||||
public static final int BIGNUMBER_ERROR = 0;
|
||||
|
||||
public static final int BIGNUMBER_STAR = 1;
|
||||
|
||||
public static final int BIGNUMBER_POSIX = 2;
|
||||
|
||||
private long currSize;
|
||||
|
||||
private String currName;
|
||||
|
||||
private long currBytes;
|
||||
|
||||
private final byte[] recordBuf;
|
||||
|
||||
private int assemLen;
|
||||
|
||||
private final byte[] assemBuf;
|
||||
|
||||
private int longFileMode = 0;
|
||||
|
||||
private int bigNumberMode = 0;
|
||||
|
||||
private int recordsWritten;
|
||||
|
||||
private final int recordsPerBlock;
|
||||
|
||||
private final int recordSize;
|
||||
|
||||
private boolean closed = false;
|
||||
|
||||
private boolean haveUnclosedEntry = false;
|
||||
|
||||
private boolean finished = false;
|
||||
|
||||
private final OutputStream out;
|
||||
|
||||
private final ZipEncoding encoding;
|
||||
|
||||
private boolean addPaxHeadersForNonAsciiNames = false;
|
||||
|
||||
private static final ZipEncoding ASCII = ZipEncodingHelper.getZipEncoding("ASCII");
|
||||
|
||||
public TarArchiveOutputStream(OutputStream os) {
|
||||
this(os, 10240, 512);
|
||||
}
|
||||
|
||||
public TarArchiveOutputStream(OutputStream os, String encoding) {
|
||||
this(os, 10240, 512, encoding);
|
||||
}
|
||||
|
||||
public TarArchiveOutputStream(OutputStream os, int blockSize) {
|
||||
this(os, blockSize, 512);
|
||||
}
|
||||
|
||||
public TarArchiveOutputStream(OutputStream os, int blockSize, String encoding) {
|
||||
this(os, blockSize, 512, encoding);
|
||||
}
|
||||
|
||||
public TarArchiveOutputStream(OutputStream os, int blockSize, int recordSize) {
|
||||
this(os, blockSize, recordSize, null);
|
||||
}
|
||||
|
||||
public TarArchiveOutputStream(OutputStream os, int blockSize, int recordSize, String encoding) {
|
||||
this.out = new CountingOutputStream(os);
|
||||
this.encoding = ZipEncodingHelper.getZipEncoding(encoding);
|
||||
this.assemLen = 0;
|
||||
this.assemBuf = new byte[recordSize];
|
||||
this.recordBuf = new byte[recordSize];
|
||||
this.recordSize = recordSize;
|
||||
this.recordsPerBlock = blockSize / recordSize;
|
||||
}
|
||||
|
||||
public void setLongFileMode(int longFileMode) {
|
||||
this.longFileMode = longFileMode;
|
||||
}
|
||||
|
||||
public void setBigNumberMode(int bigNumberMode) {
|
||||
this.bigNumberMode = bigNumberMode;
|
||||
}
|
||||
|
||||
public void setAddPaxHeadersForNonAsciiNames(boolean b) {
|
||||
this.addPaxHeadersForNonAsciiNames = b;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getCount() {
|
||||
return (int)getBytesWritten();
|
||||
}
|
||||
|
||||
public long getBytesWritten() {
|
||||
return ((CountingOutputStream)this.out).getBytesWritten();
|
||||
}
|
||||
|
||||
public void finish() throws IOException {
|
||||
if (this.finished)
|
||||
throw new IOException("This archive has already been finished");
|
||||
if (this.haveUnclosedEntry)
|
||||
throw new IOException("This archives contains unclosed entries.");
|
||||
writeEOFRecord();
|
||||
writeEOFRecord();
|
||||
padAsNeeded();
|
||||
this.out.flush();
|
||||
this.finished = true;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (!this.finished)
|
||||
finish();
|
||||
if (!this.closed) {
|
||||
this.out.close();
|
||||
this.closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public int getRecordSize() {
|
||||
return this.recordSize;
|
||||
}
|
||||
|
||||
public void putArchiveEntry(ArchiveEntry archiveEntry) throws IOException {
|
||||
if (this.finished)
|
||||
throw new IOException("Stream has already been finished");
|
||||
TarArchiveEntry entry = (TarArchiveEntry)archiveEntry;
|
||||
Map<String, String> paxHeaders = new HashMap<String, String>();
|
||||
String entryName = entry.getName();
|
||||
boolean paxHeaderContainsPath = handleLongName(entryName, paxHeaders, "path", (byte)76, "file name");
|
||||
String linkName = entry.getLinkName();
|
||||
boolean paxHeaderContainsLinkPath = (linkName != null && linkName.length() > 0 && handleLongName(linkName, paxHeaders, "linkpath", (byte)75, "link name"));
|
||||
if (this.bigNumberMode == 2) {
|
||||
addPaxHeadersForBigNumbers(paxHeaders, entry);
|
||||
} else if (this.bigNumberMode != 1) {
|
||||
failForBigNumbers(entry);
|
||||
}
|
||||
if (this.addPaxHeadersForNonAsciiNames && !paxHeaderContainsPath && !ASCII.canEncode(entryName))
|
||||
paxHeaders.put("path", entryName);
|
||||
if (this.addPaxHeadersForNonAsciiNames && !paxHeaderContainsLinkPath && (entry.isLink() || entry.isSymbolicLink()) && !ASCII.canEncode(linkName))
|
||||
paxHeaders.put("linkpath", linkName);
|
||||
if (paxHeaders.size() > 0)
|
||||
writePaxHeaders(entryName, paxHeaders);
|
||||
entry.writeEntryHeader(this.recordBuf, this.encoding, (this.bigNumberMode == 1));
|
||||
writeRecord(this.recordBuf);
|
||||
this.currBytes = 0L;
|
||||
if (entry.isDirectory()) {
|
||||
this.currSize = 0L;
|
||||
} else {
|
||||
this.currSize = entry.getSize();
|
||||
}
|
||||
this.currName = entryName;
|
||||
this.haveUnclosedEntry = true;
|
||||
}
|
||||
|
||||
public void closeArchiveEntry() throws IOException {
|
||||
if (this.finished)
|
||||
throw new IOException("Stream has already been finished");
|
||||
if (!this.haveUnclosedEntry)
|
||||
throw new IOException("No current entry to close");
|
||||
if (this.assemLen > 0) {
|
||||
for (int i = this.assemLen; i < this.assemBuf.length; i++)
|
||||
this.assemBuf[i] = 0;
|
||||
writeRecord(this.assemBuf);
|
||||
this.currBytes += (long)this.assemLen;
|
||||
this.assemLen = 0;
|
||||
}
|
||||
if (this.currBytes < this.currSize)
|
||||
throw new IOException("entry '" + this.currName + "' closed at '" + this.currBytes + "' before the '" + this.currSize + "' bytes specified in the header were written");
|
||||
this.haveUnclosedEntry = false;
|
||||
}
|
||||
|
||||
public void write(byte[] wBuf, int wOffset, int numToWrite) throws IOException {
|
||||
if (!this.haveUnclosedEntry)
|
||||
throw new IllegalStateException("No current tar entry");
|
||||
if (this.currBytes + (long)numToWrite > this.currSize)
|
||||
throw new IOException("request to write '" + numToWrite + "' bytes exceeds size in header of '" + this.currSize + "' bytes for entry '" + this.currName + "'");
|
||||
if (this.assemLen > 0)
|
||||
if (this.assemLen + numToWrite >= this.recordBuf.length) {
|
||||
int aLen = this.recordBuf.length - this.assemLen;
|
||||
System.arraycopy(this.assemBuf, 0, this.recordBuf, 0, this.assemLen);
|
||||
System.arraycopy(wBuf, wOffset, this.recordBuf, this.assemLen, aLen);
|
||||
writeRecord(this.recordBuf);
|
||||
this.currBytes += (long)this.recordBuf.length;
|
||||
wOffset += aLen;
|
||||
numToWrite -= aLen;
|
||||
this.assemLen = 0;
|
||||
} else {
|
||||
System.arraycopy(wBuf, wOffset, this.assemBuf, this.assemLen, numToWrite);
|
||||
wOffset += numToWrite;
|
||||
this.assemLen += numToWrite;
|
||||
numToWrite = 0;
|
||||
}
|
||||
while (numToWrite > 0) {
|
||||
if (numToWrite < this.recordBuf.length) {
|
||||
System.arraycopy(wBuf, wOffset, this.assemBuf, this.assemLen, numToWrite);
|
||||
this.assemLen += numToWrite;
|
||||
break;
|
||||
}
|
||||
writeRecord(wBuf, wOffset);
|
||||
int num = this.recordBuf.length;
|
||||
this.currBytes += (long)num;
|
||||
numToWrite -= num;
|
||||
wOffset += num;
|
||||
}
|
||||
}
|
||||
|
||||
void writePaxHeaders(String entryName, Map<String, String> headers) throws IOException {
|
||||
String name = "./PaxHeaders.X/" + stripTo7Bits(entryName);
|
||||
if (name.length() >= 100)
|
||||
name = name.substring(0, 99);
|
||||
TarArchiveEntry pex = new TarArchiveEntry(name, (byte)120);
|
||||
StringWriter w = new StringWriter();
|
||||
for (Map.Entry<String, String> h : headers.entrySet()) {
|
||||
String key = h.getKey();
|
||||
String value = h.getValue();
|
||||
int len = key.length() + value.length() + 3 + 2;
|
||||
String line = len + " " + key + "=" + value + "\n";
|
||||
int actualLength = (line.getBytes("UTF-8")).length;
|
||||
while (len != actualLength) {
|
||||
len = actualLength;
|
||||
line = len + " " + key + "=" + value + "\n";
|
||||
actualLength = (line.getBytes("UTF-8")).length;
|
||||
}
|
||||
w.write(line);
|
||||
}
|
||||
byte[] data = w.toString().getBytes("UTF-8");
|
||||
pex.setSize((long)data.length);
|
||||
putArchiveEntry(pex);
|
||||
write(data);
|
||||
closeArchiveEntry();
|
||||
}
|
||||
|
||||
private String stripTo7Bits(String name) {
|
||||
int length = name.length();
|
||||
StringBuilder result = new StringBuilder(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
char stripped = (char)(name.charAt(i) & 0x7F);
|
||||
if (shouldBeReplaced(stripped)) {
|
||||
result.append("_");
|
||||
} else {
|
||||
result.append(stripped);
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private boolean shouldBeReplaced(char c) {
|
||||
return (c == '\000' || c == '/' || c == '\\');
|
||||
}
|
||||
|
||||
private void writeEOFRecord() throws IOException {
|
||||
Arrays.fill(this.recordBuf, (byte)0);
|
||||
writeRecord(this.recordBuf);
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
this.out.flush();
|
||||
}
|
||||
|
||||
public ArchiveEntry createArchiveEntry(File inputFile, String entryName) throws IOException {
|
||||
if (this.finished)
|
||||
throw new IOException("Stream has already been finished");
|
||||
return new TarArchiveEntry(inputFile, entryName);
|
||||
}
|
||||
|
||||
private void writeRecord(byte[] record) throws IOException {
|
||||
if (record.length != this.recordSize)
|
||||
throw new IOException("record to write has length '" + record.length + "' which is not the record size of '" + this.recordSize + "'");
|
||||
this.out.write(record);
|
||||
this.recordsWritten++;
|
||||
}
|
||||
|
||||
private void writeRecord(byte[] buf, int offset) throws IOException {
|
||||
if (offset + this.recordSize > buf.length)
|
||||
throw new IOException("record has length '" + buf.length + "' with offset '" + offset + "' which is less than the record size of '" + this.recordSize + "'");
|
||||
this.out.write(buf, offset, this.recordSize);
|
||||
this.recordsWritten++;
|
||||
}
|
||||
|
||||
private void padAsNeeded() throws IOException {
|
||||
int start = this.recordsWritten % this.recordsPerBlock;
|
||||
if (start != 0)
|
||||
for (int i = start; i < this.recordsPerBlock; i++)
|
||||
writeEOFRecord();
|
||||
}
|
||||
|
||||
private void addPaxHeadersForBigNumbers(Map<String, String> paxHeaders, TarArchiveEntry entry) {
|
||||
addPaxHeaderForBigNumber(paxHeaders, "size", entry.getSize(), 8589934591L);
|
||||
addPaxHeaderForBigNumber(paxHeaders, "gid", (long)entry.getGroupId(), 2097151L);
|
||||
addPaxHeaderForBigNumber(paxHeaders, "mtime", entry.getModTime().getTime() / 1000L, 8589934591L);
|
||||
addPaxHeaderForBigNumber(paxHeaders, "uid", (long)entry.getUserId(), 2097151L);
|
||||
addPaxHeaderForBigNumber(paxHeaders, "SCHILY.devmajor", (long)entry.getDevMajor(), 2097151L);
|
||||
addPaxHeaderForBigNumber(paxHeaders, "SCHILY.devminor", (long)entry.getDevMinor(), 2097151L);
|
||||
failForBigNumber("mode", (long)entry.getMode(), 2097151L);
|
||||
}
|
||||
|
||||
private void addPaxHeaderForBigNumber(Map<String, String> paxHeaders, String header, long value, long maxValue) {
|
||||
if (value < 0L || value > maxValue)
|
||||
paxHeaders.put(header, String.valueOf(value));
|
||||
}
|
||||
|
||||
private void failForBigNumbers(TarArchiveEntry entry) {
|
||||
failForBigNumber("entry size", entry.getSize(), 8589934591L);
|
||||
failForBigNumber("group id", (long)entry.getGroupId(), 2097151L);
|
||||
failForBigNumber("last modification time", entry.getModTime().getTime() / 1000L, 8589934591L);
|
||||
failForBigNumber("user id", (long)entry.getUserId(), 2097151L);
|
||||
failForBigNumber("mode", (long)entry.getMode(), 2097151L);
|
||||
failForBigNumber("major device number", (long)entry.getDevMajor(), 2097151L);
|
||||
failForBigNumber("minor device number", (long)entry.getDevMinor(), 2097151L);
|
||||
}
|
||||
|
||||
private void failForBigNumber(String field, long value, long maxValue) {
|
||||
if (value < 0L || value > maxValue)
|
||||
throw new RuntimeException(field + " '" + value + "' is too big ( > " + maxValue + " )");
|
||||
}
|
||||
|
||||
private boolean handleLongName(String name, Map<String, String> paxHeaders, String paxHeaderName, byte linkType, String fieldName) throws IOException {
|
||||
ByteBuffer encodedName = this.encoding.encode(name);
|
||||
int len = encodedName.limit() - encodedName.position();
|
||||
if (len >= 100) {
|
||||
if (this.longFileMode == 3) {
|
||||
paxHeaders.put(paxHeaderName, name);
|
||||
return true;
|
||||
}
|
||||
if (this.longFileMode == 2) {
|
||||
TarArchiveEntry longLinkEntry = new TarArchiveEntry("././@LongLink", linkType);
|
||||
longLinkEntry.setSize((long)(len + 1));
|
||||
putArchiveEntry(longLinkEntry);
|
||||
write(encodedName.array(), encodedName.arrayOffset(), len);
|
||||
write(0);
|
||||
closeArchiveEntry();
|
||||
} else if (this.longFileMode != 1) {
|
||||
throw new RuntimeException(fieldName + " '" + name + "' is too long ( > " + 'd' + " bytes)");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package org.apache.commons.compress.archivers.tar;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class TarArchiveSparseEntry implements TarConstants {
|
||||
private final boolean isExtended;
|
||||
|
||||
public TarArchiveSparseEntry(byte[] headerBuf) throws IOException {
|
||||
int offset = 0;
|
||||
offset += 504;
|
||||
this.isExtended = TarUtils.parseBoolean(headerBuf, offset);
|
||||
}
|
||||
|
||||
public boolean isExtended() {
|
||||
return this.isExtended;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
package org.apache.commons.compress.archivers.tar;
|
||||
|
||||
public interface TarConstants {
|
||||
public static final int DEFAULT_RCDSIZE = 512;
|
||||
|
||||
public static final int DEFAULT_BLKSIZE = 10240;
|
||||
|
||||
public static final int FORMAT_OLDGNU = 2;
|
||||
|
||||
public static final int FORMAT_POSIX = 3;
|
||||
|
||||
public static final int NAMELEN = 100;
|
||||
|
||||
public static final int MODELEN = 8;
|
||||
|
||||
public static final int UIDLEN = 8;
|
||||
|
||||
public static final int GIDLEN = 8;
|
||||
|
||||
public static final long MAXID = 2097151L;
|
||||
|
||||
public static final int CHKSUMLEN = 8;
|
||||
|
||||
public static final int CHKSUM_OFFSET = 148;
|
||||
|
||||
public static final int SIZELEN = 12;
|
||||
|
||||
public static final long MAXSIZE = 8589934591L;
|
||||
|
||||
public static final int MAGIC_OFFSET = 257;
|
||||
|
||||
public static final int MAGICLEN = 6;
|
||||
|
||||
public static final int VERSION_OFFSET = 263;
|
||||
|
||||
public static final int VERSIONLEN = 2;
|
||||
|
||||
public static final int MODTIMELEN = 12;
|
||||
|
||||
public static final int UNAMELEN = 32;
|
||||
|
||||
public static final int GNAMELEN = 32;
|
||||
|
||||
public static final int DEVLEN = 8;
|
||||
|
||||
public static final int PREFIXLEN = 155;
|
||||
|
||||
public static final int ATIMELEN_GNU = 12;
|
||||
|
||||
public static final int CTIMELEN_GNU = 12;
|
||||
|
||||
public static final int OFFSETLEN_GNU = 12;
|
||||
|
||||
public static final int LONGNAMESLEN_GNU = 4;
|
||||
|
||||
public static final int PAD2LEN_GNU = 1;
|
||||
|
||||
public static final int SPARSELEN_GNU = 96;
|
||||
|
||||
public static final int ISEXTENDEDLEN_GNU = 1;
|
||||
|
||||
public static final int REALSIZELEN_GNU = 12;
|
||||
|
||||
public static final int SPARSELEN_GNU_SPARSE = 504;
|
||||
|
||||
public static final int ISEXTENDEDLEN_GNU_SPARSE = 1;
|
||||
|
||||
public static final byte LF_OLDNORM = 0;
|
||||
|
||||
public static final byte LF_NORMAL = 48;
|
||||
|
||||
public static final byte LF_LINK = 49;
|
||||
|
||||
public static final byte LF_SYMLINK = 50;
|
||||
|
||||
public static final byte LF_CHR = 51;
|
||||
|
||||
public static final byte LF_BLK = 52;
|
||||
|
||||
public static final byte LF_DIR = 53;
|
||||
|
||||
public static final byte LF_FIFO = 54;
|
||||
|
||||
public static final byte LF_CONTIG = 55;
|
||||
|
||||
public static final byte LF_GNUTYPE_LONGLINK = 75;
|
||||
|
||||
public static final byte LF_GNUTYPE_LONGNAME = 76;
|
||||
|
||||
public static final byte LF_GNUTYPE_SPARSE = 83;
|
||||
|
||||
public static final byte LF_PAX_EXTENDED_HEADER_LC = 120;
|
||||
|
||||
public static final byte LF_PAX_EXTENDED_HEADER_UC = 88;
|
||||
|
||||
public static final byte LF_PAX_GLOBAL_EXTENDED_HEADER = 103;
|
||||
|
||||
public static final String MAGIC_POSIX = "ustar\000";
|
||||
|
||||
public static final String VERSION_POSIX = "00";
|
||||
|
||||
public static final String MAGIC_GNU = "ustar ";
|
||||
|
||||
public static final String VERSION_GNU_SPACE = " \000";
|
||||
|
||||
public static final String VERSION_GNU_ZERO = "0\000";
|
||||
|
||||
public static final String MAGIC_ANT = "ustar\000";
|
||||
|
||||
public static final String VERSION_ANT = "\000\000";
|
||||
|
||||
public static final String GNU_LONGLINK = "././@LongLink";
|
||||
}
|
||||
|
|
@ -0,0 +1,265 @@
|
|||
package org.apache.commons.compress.archivers.tar;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import org.apache.commons.compress.archivers.zip.ZipEncoding;
|
||||
import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
|
||||
|
||||
public class TarUtils {
|
||||
private static final int BYTE_MASK = 255;
|
||||
|
||||
static final ZipEncoding DEFAULT_ENCODING = ZipEncodingHelper.getZipEncoding(null);
|
||||
|
||||
static final ZipEncoding FALLBACK_ENCODING = new ZipEncoding() {
|
||||
public boolean canEncode(String name) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public ByteBuffer encode(String name) {
|
||||
int length = name.length();
|
||||
byte[] buf = new byte[length];
|
||||
for (int i = 0; i < length; i++)
|
||||
buf[i] = (byte)name.charAt(i);
|
||||
return ByteBuffer.wrap(buf);
|
||||
}
|
||||
|
||||
public String decode(byte[] buffer) {
|
||||
int length = buffer.length;
|
||||
StringBuilder result = new StringBuilder(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
byte b = buffer[i];
|
||||
if (b == 0)
|
||||
break;
|
||||
result.append((char)(b & 0xFF));
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
};
|
||||
|
||||
public static long parseOctal(byte[] buffer, int offset, int length) {
|
||||
long result = 0L;
|
||||
int end = offset + length;
|
||||
int start = offset;
|
||||
if (length < 2)
|
||||
throw new IllegalArgumentException("Length " + length + " must be at least 2");
|
||||
if (buffer[start] == 0)
|
||||
return 0L;
|
||||
while (start < end &&
|
||||
buffer[start] == 32)
|
||||
start++;
|
||||
byte trailer = buffer[end - 1];
|
||||
while (start < end && (trailer == 0 || trailer == 32)) {
|
||||
end--;
|
||||
trailer = buffer[end - 1];
|
||||
}
|
||||
for (; start < end; start++) {
|
||||
byte currentByte = buffer[start];
|
||||
if (currentByte < 48 || currentByte > 55)
|
||||
throw new IllegalArgumentException(exceptionMessage(buffer, offset, length, start, currentByte));
|
||||
result = (result << 3L) + (long)(currentByte - 48);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static long parseOctalOrBinary(byte[] buffer, int offset, int length) {
|
||||
if ((buffer[offset] & 0x80) == 0)
|
||||
return parseOctal(buffer, offset, length);
|
||||
boolean negative = (buffer[offset] == -1);
|
||||
if (length < 9)
|
||||
return parseBinaryLong(buffer, offset, length, negative);
|
||||
return parseBinaryBigInteger(buffer, offset, length, negative);
|
||||
}
|
||||
|
||||
private static long parseBinaryLong(byte[] buffer, int offset, int length, boolean negative) {
|
||||
if (length >= 9)
|
||||
throw new IllegalArgumentException("At offset " + offset + ", " + length + " byte binary number" + " exceeds maximum signed long" + " value");
|
||||
long val = 0L;
|
||||
for (int i = 1; i < length; i++)
|
||||
val = (val << 8L) + (long)(buffer[offset + i] & 0xFF);
|
||||
if (negative) {
|
||||
val--;
|
||||
val ^= (long)Math.pow(2.0D, (double)((length - 1) * 8)) - 1L;
|
||||
}
|
||||
return negative ? -val : val;
|
||||
}
|
||||
|
||||
private static long parseBinaryBigInteger(byte[] buffer, int offset, int length, boolean negative) {
|
||||
byte[] remainder = new byte[length - 1];
|
||||
System.arraycopy(buffer, offset + 1, remainder, 0, length - 1);
|
||||
BigInteger val = new BigInteger(remainder);
|
||||
if (negative)
|
||||
val = val.add(BigInteger.valueOf(-1L)).not();
|
||||
if (val.bitLength() > 63)
|
||||
throw new IllegalArgumentException("At offset " + offset + ", " + length + " byte binary number" + " exceeds maximum signed long" + " value");
|
||||
return negative ? -val.longValue() : val.longValue();
|
||||
}
|
||||
|
||||
public static boolean parseBoolean(byte[] buffer, int offset) {
|
||||
return (buffer[offset] == 1);
|
||||
}
|
||||
|
||||
private static String exceptionMessage(byte[] buffer, int offset, int length, int current, byte currentByte) {
|
||||
String string = new String(buffer, offset, length);
|
||||
string = string.replaceAll("\000", "{NUL}");
|
||||
String s = "Invalid byte " + currentByte + " at offset " + (current - offset) + " in '" + string + "' len=" + length;
|
||||
return s;
|
||||
}
|
||||
|
||||
public static String parseName(byte[] buffer, int offset, int length) {
|
||||
try {
|
||||
return parseName(buffer, offset, length, DEFAULT_ENCODING);
|
||||
} catch (IOException ex) {
|
||||
try {
|
||||
return parseName(buffer, offset, length, FALLBACK_ENCODING);
|
||||
} catch (IOException ex2) {
|
||||
throw new RuntimeException(ex2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String parseName(byte[] buffer, int offset, int length, ZipEncoding encoding) throws IOException {
|
||||
int len = length;
|
||||
for (; len > 0 &&
|
||||
buffer[offset + len - 1] == 0; len--);
|
||||
if (len > 0) {
|
||||
byte[] b = new byte[len];
|
||||
System.arraycopy(buffer, offset, b, 0, len);
|
||||
return encoding.decode(b);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static int formatNameBytes(String name, byte[] buf, int offset, int length) {
|
||||
try {
|
||||
return formatNameBytes(name, buf, offset, length, DEFAULT_ENCODING);
|
||||
} catch (IOException ex) {
|
||||
try {
|
||||
return formatNameBytes(name, buf, offset, length, FALLBACK_ENCODING);
|
||||
} catch (IOException ex2) {
|
||||
throw new RuntimeException(ex2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int formatNameBytes(String name, byte[] buf, int offset, int length, ZipEncoding encoding) throws IOException {
|
||||
int len = name.length();
|
||||
ByteBuffer b = encoding.encode(name);
|
||||
while (b.limit() > length && len > 0)
|
||||
b = encoding.encode(name.substring(0, --len));
|
||||
int limit = b.limit() - b.position();
|
||||
System.arraycopy(b.array(), b.arrayOffset(), buf, offset, limit);
|
||||
for (int i = limit; i < length; i++)
|
||||
buf[offset + i] = 0;
|
||||
return offset + length;
|
||||
}
|
||||
|
||||
public static void formatUnsignedOctalString(long value, byte[] buffer, int offset, int length) {
|
||||
int remaining = length;
|
||||
remaining--;
|
||||
if (value == 0L) {
|
||||
buffer[offset + remaining--] = 48;
|
||||
} else {
|
||||
long val = value;
|
||||
for (; remaining >= 0 && val != 0L; remaining--) {
|
||||
buffer[offset + remaining] = (byte)(48 + (byte)(int)(val & 0x7L));
|
||||
val >>>= 3L;
|
||||
}
|
||||
if (val != 0L)
|
||||
throw new IllegalArgumentException(value + "=" + Long.toOctalString(value) + " will not fit in octal number buffer of length " + length);
|
||||
}
|
||||
for (; remaining >= 0; remaining--)
|
||||
buffer[offset + remaining] = 48;
|
||||
}
|
||||
|
||||
public static int formatOctalBytes(long value, byte[] buf, int offset, int length) {
|
||||
int idx = length - 2;
|
||||
formatUnsignedOctalString(value, buf, offset, idx);
|
||||
buf[offset + idx++] = 32;
|
||||
buf[offset + idx] = 0;
|
||||
return offset + length;
|
||||
}
|
||||
|
||||
public static int formatLongOctalBytes(long value, byte[] buf, int offset, int length) {
|
||||
int idx = length - 1;
|
||||
formatUnsignedOctalString(value, buf, offset, idx);
|
||||
buf[offset + idx] = 32;
|
||||
return offset + length;
|
||||
}
|
||||
|
||||
public static int formatLongOctalOrBinaryBytes(long value, byte[] buf, int offset, int length) {
|
||||
long maxAsOctalChar = (length == 8) ? 2097151L : 8589934591L;
|
||||
boolean negative = (value < 0L);
|
||||
if (!negative && value <= maxAsOctalChar)
|
||||
return formatLongOctalBytes(value, buf, offset, length);
|
||||
if (length < 9)
|
||||
formatLongBinary(value, buf, offset, length, negative);
|
||||
formatBigIntegerBinary(value, buf, offset, length, negative);
|
||||
buf[offset] = (byte)(negative ? 255 : 128);
|
||||
return offset + length;
|
||||
}
|
||||
|
||||
private static void formatLongBinary(long value, byte[] buf, int offset, int length, boolean negative) {
|
||||
int bits = (length - 1) * 8;
|
||||
long max = 1L << bits;
|
||||
long val = Math.abs(value);
|
||||
if (val >= max)
|
||||
throw new IllegalArgumentException("Value " + value + " is too large for " + length + " byte field.");
|
||||
if (negative) {
|
||||
val ^= max - 1L;
|
||||
val |= (long)(255 << bits);
|
||||
val++;
|
||||
}
|
||||
for (int i = offset + length - 1; i >= offset; i--) {
|
||||
buf[i] = (byte)(int)val;
|
||||
val >>= 8L;
|
||||
}
|
||||
}
|
||||
|
||||
private static void formatBigIntegerBinary(long value, byte[] buf, int offset, int length, boolean negative) {
|
||||
BigInteger val = BigInteger.valueOf(value);
|
||||
byte[] b = val.toByteArray();
|
||||
int len = b.length;
|
||||
int off = offset + length - len;
|
||||
System.arraycopy(b, 0, buf, off, len);
|
||||
byte fill = (byte)(negative ? 255 : 0);
|
||||
for (int i = offset + 1; i < off; i++)
|
||||
buf[i] = fill;
|
||||
}
|
||||
|
||||
public static int formatCheckSumOctalBytes(long value, byte[] buf, int offset, int length) {
|
||||
int idx = length - 2;
|
||||
formatUnsignedOctalString(value, buf, offset, idx);
|
||||
buf[offset + idx++] = 0;
|
||||
buf[offset + idx] = 32;
|
||||
return offset + length;
|
||||
}
|
||||
|
||||
public static long computeCheckSum(byte[] buf) {
|
||||
long sum = 0L;
|
||||
for (byte element : buf)
|
||||
sum += (long)(0xFF & element);
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static boolean verifyCheckSum(byte[] header) {
|
||||
long storedSum = 0L;
|
||||
long unsignedSum = 0L;
|
||||
long signedSum = 0L;
|
||||
int digits = 0;
|
||||
for (int i = 0; i < header.length; i++) {
|
||||
byte b = header[i];
|
||||
if (148 <= i && i < 156) {
|
||||
if (48 <= b && b <= 55 && digits++ < 6) {
|
||||
storedSum = storedSum * 8L + (long)b - 48L;
|
||||
} else if (digits > 0) {
|
||||
digits = 6;
|
||||
}
|
||||
b = 32;
|
||||
}
|
||||
unsignedSum += (long)(0xFF & b);
|
||||
signedSum += (long)b;
|
||||
}
|
||||
return (storedSum == unsignedSum || storedSum == signedSum || storedSum > unsignedSum);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.zip.CRC32;
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
public abstract class AbstractUnicodeExtraField implements ZipExtraField {
|
||||
private long nameCRC32;
|
||||
|
||||
private byte[] unicodeName;
|
||||
|
||||
private byte[] data;
|
||||
|
||||
protected AbstractUnicodeExtraField() {}
|
||||
|
||||
protected AbstractUnicodeExtraField(String text, byte[] bytes, int off, int len) {
|
||||
CRC32 crc32 = new CRC32();
|
||||
crc32.update(bytes, off, len);
|
||||
this.nameCRC32 = crc32.getValue();
|
||||
try {
|
||||
this.unicodeName = text.getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("FATAL: UTF-8 encoding not supported.", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected AbstractUnicodeExtraField(String text, byte[] bytes) {
|
||||
this(text, bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
private void assembleData() {
|
||||
if (this.unicodeName == null)
|
||||
return;
|
||||
this.data = new byte[5 + this.unicodeName.length];
|
||||
this.data[0] = 1;
|
||||
System.arraycopy(ZipLong.getBytes(this.nameCRC32), 0, this.data, 1, 4);
|
||||
System.arraycopy(this.unicodeName, 0, this.data, 5, this.unicodeName.length);
|
||||
}
|
||||
|
||||
public long getNameCRC32() {
|
||||
return this.nameCRC32;
|
||||
}
|
||||
|
||||
public void setNameCRC32(long nameCRC32) {
|
||||
this.nameCRC32 = nameCRC32;
|
||||
this.data = null;
|
||||
}
|
||||
|
||||
public byte[] getUnicodeName() {
|
||||
byte[] b = null;
|
||||
if (this.unicodeName != null) {
|
||||
b = new byte[this.unicodeName.length];
|
||||
System.arraycopy(this.unicodeName, 0, b, 0, b.length);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
public void setUnicodeName(byte[] unicodeName) {
|
||||
if (unicodeName != null) {
|
||||
this.unicodeName = new byte[unicodeName.length];
|
||||
System.arraycopy(unicodeName, 0, this.unicodeName, 0, unicodeName.length);
|
||||
} else {
|
||||
this.unicodeName = null;
|
||||
}
|
||||
this.data = null;
|
||||
}
|
||||
|
||||
public byte[] getCentralDirectoryData() {
|
||||
if (this.data == null)
|
||||
assembleData();
|
||||
byte[] b = null;
|
||||
if (this.data != null) {
|
||||
b = new byte[this.data.length];
|
||||
System.arraycopy(this.data, 0, b, 0, b.length);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
public ZipShort getCentralDirectoryLength() {
|
||||
if (this.data == null)
|
||||
assembleData();
|
||||
return new ZipShort((this.data != null) ? this.data.length : 0);
|
||||
}
|
||||
|
||||
public byte[] getLocalFileDataData() {
|
||||
return getCentralDirectoryData();
|
||||
}
|
||||
|
||||
public ZipShort getLocalFileDataLength() {
|
||||
return getCentralDirectoryLength();
|
||||
}
|
||||
|
||||
public void parseFromLocalFileData(byte[] buffer, int offset, int length) throws ZipException {
|
||||
if (length < 5)
|
||||
throw new ZipException("UniCode path extra data must have at least 5 bytes.");
|
||||
int version = buffer[offset];
|
||||
if (version != 1)
|
||||
throw new ZipException("Unsupported version [" + version + "] for UniCode path extra data.");
|
||||
this.nameCRC32 = ZipLong.getValue(buffer, offset + 1);
|
||||
this.unicodeName = new byte[length - 5];
|
||||
System.arraycopy(buffer, offset + 5, this.unicodeName, 0, length - 5);
|
||||
this.data = null;
|
||||
}
|
||||
|
||||
public void parseFromCentralDirectoryData(byte[] buffer, int offset, int length) throws ZipException {
|
||||
parseFromLocalFileData(buffer, offset, length);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.util.zip.CRC32;
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
public class AsiExtraField implements ZipExtraField, UnixStat, Cloneable {
|
||||
private static final ZipShort HEADER_ID = new ZipShort(30062);
|
||||
|
||||
private static final int WORD = 4;
|
||||
|
||||
private int mode = 0;
|
||||
|
||||
private int uid = 0;
|
||||
|
||||
private int gid = 0;
|
||||
|
||||
private String link = "";
|
||||
|
||||
private boolean dirFlag = false;
|
||||
|
||||
private CRC32 crc = new CRC32();
|
||||
|
||||
public ZipShort getHeaderId() {
|
||||
return HEADER_ID;
|
||||
}
|
||||
|
||||
public ZipShort getLocalFileDataLength() {
|
||||
return new ZipShort(14 + (getLinkedFile().getBytes()).length);
|
||||
}
|
||||
|
||||
public ZipShort getCentralDirectoryLength() {
|
||||
return getLocalFileDataLength();
|
||||
}
|
||||
|
||||
public byte[] getLocalFileDataData() {
|
||||
byte[] data = new byte[getLocalFileDataLength().getValue() - 4];
|
||||
System.arraycopy(ZipShort.getBytes(getMode()), 0, data, 0, 2);
|
||||
byte[] linkArray = getLinkedFile().getBytes();
|
||||
System.arraycopy(ZipLong.getBytes((long)linkArray.length), 0, data, 2, 4);
|
||||
System.arraycopy(ZipShort.getBytes(getUserId()), 0, data, 6, 2);
|
||||
System.arraycopy(ZipShort.getBytes(getGroupId()), 0, data, 8, 2);
|
||||
System.arraycopy(linkArray, 0, data, 10, linkArray.length);
|
||||
this.crc.reset();
|
||||
this.crc.update(data);
|
||||
long checksum = this.crc.getValue();
|
||||
byte[] result = new byte[data.length + 4];
|
||||
System.arraycopy(ZipLong.getBytes(checksum), 0, result, 0, 4);
|
||||
System.arraycopy(data, 0, result, 4, data.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
public byte[] getCentralDirectoryData() {
|
||||
return getLocalFileDataData();
|
||||
}
|
||||
|
||||
public void setUserId(int uid) {
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
public int getUserId() {
|
||||
return this.uid;
|
||||
}
|
||||
|
||||
public void setGroupId(int gid) {
|
||||
this.gid = gid;
|
||||
}
|
||||
|
||||
public int getGroupId() {
|
||||
return this.gid;
|
||||
}
|
||||
|
||||
public void setLinkedFile(String name) {
|
||||
this.link = name;
|
||||
this.mode = getMode(this.mode);
|
||||
}
|
||||
|
||||
public String getLinkedFile() {
|
||||
return this.link;
|
||||
}
|
||||
|
||||
public boolean isLink() {
|
||||
return (getLinkedFile().length() != 0);
|
||||
}
|
||||
|
||||
public void setMode(int mode) {
|
||||
this.mode = getMode(mode);
|
||||
}
|
||||
|
||||
public int getMode() {
|
||||
return this.mode;
|
||||
}
|
||||
|
||||
public void setDirectory(boolean dirFlag) {
|
||||
this.dirFlag = dirFlag;
|
||||
this.mode = getMode(this.mode);
|
||||
}
|
||||
|
||||
public boolean isDirectory() {
|
||||
return (this.dirFlag && !isLink());
|
||||
}
|
||||
|
||||
public void parseFromLocalFileData(byte[] data, int offset, int length) throws ZipException {
|
||||
long givenChecksum = ZipLong.getValue(data, offset);
|
||||
byte[] tmp = new byte[length - 4];
|
||||
System.arraycopy(data, offset + 4, tmp, 0, length - 4);
|
||||
this.crc.reset();
|
||||
this.crc.update(tmp);
|
||||
long realChecksum = this.crc.getValue();
|
||||
if (givenChecksum != realChecksum)
|
||||
throw new ZipException("bad CRC checksum " + Long.toHexString(givenChecksum) + " instead of " + Long.toHexString(realChecksum));
|
||||
int newMode = ZipShort.getValue(tmp, 0);
|
||||
byte[] linkArray = new byte[(int)ZipLong.getValue(tmp, 2)];
|
||||
this.uid = ZipShort.getValue(tmp, 6);
|
||||
this.gid = ZipShort.getValue(tmp, 8);
|
||||
if (linkArray.length == 0) {
|
||||
this.link = "";
|
||||
} else {
|
||||
System.arraycopy(tmp, 10, linkArray, 0, linkArray.length);
|
||||
this.link = new String(linkArray);
|
||||
}
|
||||
setDirectory(((newMode & 0x4000) != 0));
|
||||
setMode(newMode);
|
||||
}
|
||||
|
||||
public void parseFromCentralDirectoryData(byte[] buffer, int offset, int length) throws ZipException {
|
||||
parseFromLocalFileData(buffer, offset, length);
|
||||
}
|
||||
|
||||
protected int getMode(int mode) {
|
||||
int type = 32768;
|
||||
if (isLink()) {
|
||||
type = 40960;
|
||||
} else if (isDirectory()) {
|
||||
type = 16384;
|
||||
}
|
||||
return type | mode & 0xFFF;
|
||||
}
|
||||
|
||||
public Object clone() {
|
||||
try {
|
||||
AsiExtraField cloned = (AsiExtraField)super.clone();
|
||||
cloned.crc = new CRC32();
|
||||
return cloned;
|
||||
} catch (CloneNotSupportedException cnfe) {
|
||||
throw new RuntimeException(cnfe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
class BinaryTree {
|
||||
private static final int UNDEFINED = -1;
|
||||
|
||||
private static final int NODE = -2;
|
||||
|
||||
private final int[] tree;
|
||||
|
||||
public BinaryTree(int depth) {
|
||||
this.tree = new int[(1 << depth + 1) - 1];
|
||||
Arrays.fill(this.tree, -1);
|
||||
}
|
||||
|
||||
public void addLeaf(int node, int path, int depth, int value) {
|
||||
if (depth == 0) {
|
||||
if (this.tree[node] == -1) {
|
||||
this.tree[node] = value;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Tree value at index " + node + " has already been assigned (" + this.tree[node] + ")");
|
||||
}
|
||||
} else {
|
||||
this.tree[node] = -2;
|
||||
int nextChild = 2 * node + 1 + (path & 0x1);
|
||||
addLeaf(nextChild, path >>> 1, depth - 1, value);
|
||||
}
|
||||
}
|
||||
|
||||
public int read(BitStream stream) throws IOException {
|
||||
int bit, value;
|
||||
int currentIndex = 0;
|
||||
while (true) {
|
||||
bit = stream.nextBit();
|
||||
if (bit == -1)
|
||||
return -1;
|
||||
int childIndex = 2 * currentIndex + 1 + bit;
|
||||
value = this.tree[childIndex];
|
||||
if (value == -2) {
|
||||
currentIndex = childIndex;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (value != -1)
|
||||
return value;
|
||||
throw new IOException("The child " + bit + " of node at index " + currentIndex + " is not defined");
|
||||
}
|
||||
|
||||
static BinaryTree decode(InputStream in, int totalNumberOfValues) throws IOException {
|
||||
int size = in.read() + 1;
|
||||
if (size == 0)
|
||||
throw new IOException("Cannot read the size of the encoded tree, unexpected end of stream");
|
||||
byte[] encodedTree = new byte[size];
|
||||
new DataInputStream(in).readFully(encodedTree);
|
||||
int maxLength = 0;
|
||||
int[] originalBitLengths = new int[totalNumberOfValues];
|
||||
int pos = 0;
|
||||
for (byte b : encodedTree) {
|
||||
int numberOfValues = ((b & 0xF0) >> 4) + 1;
|
||||
int bitLength = (b & 0xF) + 1;
|
||||
for (int n = 0; n < numberOfValues; n++)
|
||||
originalBitLengths[pos++] = bitLength;
|
||||
maxLength = Math.max(maxLength, bitLength);
|
||||
}
|
||||
int[] permutation = new int[originalBitLengths.length];
|
||||
for (int k = 0; k < permutation.length; k++)
|
||||
permutation[k] = k;
|
||||
int c = 0;
|
||||
int[] sortedBitLengths = new int[originalBitLengths.length];
|
||||
for (int j = 0; j < originalBitLengths.length; j++) {
|
||||
for (int l = 0; l < originalBitLengths.length; l++) {
|
||||
if (originalBitLengths[l] == j) {
|
||||
sortedBitLengths[c] = j;
|
||||
permutation[c] = l;
|
||||
c++;
|
||||
}
|
||||
}
|
||||
}
|
||||
int code = 0;
|
||||
int codeIncrement = 0;
|
||||
int lastBitLength = 0;
|
||||
int[] codes = new int[totalNumberOfValues];
|
||||
for (int i = totalNumberOfValues - 1; i >= 0; i--) {
|
||||
code += codeIncrement;
|
||||
if (sortedBitLengths[i] != lastBitLength) {
|
||||
lastBitLength = sortedBitLengths[i];
|
||||
codeIncrement = 1 << 16 - lastBitLength;
|
||||
}
|
||||
codes[permutation[i]] = code;
|
||||
}
|
||||
BinaryTree tree = new BinaryTree(maxLength);
|
||||
for (int m = 0; m < codes.length; m++) {
|
||||
int bitLength = originalBitLengths[m];
|
||||
if (bitLength > 0)
|
||||
tree.addLeaf(0, Integer.reverse(codes[m] << 16), bitLength, m);
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
class BitStream {
|
||||
private final InputStream in;
|
||||
|
||||
private long bitCache;
|
||||
|
||||
private int bitCacheSize;
|
||||
|
||||
private static final int[] MASKS = new int[] { 0, 1, 3, 7, 15, 31, 63, 127, 255 };
|
||||
|
||||
BitStream(InputStream in) {
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
private boolean fillCache() throws IOException {
|
||||
boolean filled = false;
|
||||
while (this.bitCacheSize <= 56) {
|
||||
long nextByte = (long)this.in.read();
|
||||
if (nextByte == -1L)
|
||||
break;
|
||||
filled = true;
|
||||
this.bitCache |= nextByte << this.bitCacheSize;
|
||||
this.bitCacheSize += 8;
|
||||
}
|
||||
return filled;
|
||||
}
|
||||
|
||||
int nextBit() throws IOException {
|
||||
if (this.bitCacheSize == 0 && !fillCache())
|
||||
return -1;
|
||||
int bit = (int)(this.bitCache & 0x1L);
|
||||
this.bitCache >>>= 1L;
|
||||
this.bitCacheSize--;
|
||||
return bit;
|
||||
}
|
||||
|
||||
int nextBits(int n) throws IOException {
|
||||
if (this.bitCacheSize < n && !fillCache())
|
||||
return -1;
|
||||
int bits = (int)(this.bitCache & (long)MASKS[n]);
|
||||
this.bitCache >>>= n;
|
||||
this.bitCacheSize -= n;
|
||||
return bits;
|
||||
}
|
||||
|
||||
int nextByte() throws IOException {
|
||||
return nextBits(8);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
class CircularBuffer {
|
||||
private final int size;
|
||||
|
||||
private final byte[] buffer;
|
||||
|
||||
private int readIndex;
|
||||
|
||||
private int writeIndex;
|
||||
|
||||
CircularBuffer(int size) {
|
||||
this.size = size;
|
||||
this.buffer = new byte[size];
|
||||
}
|
||||
|
||||
public boolean available() {
|
||||
return (this.readIndex != this.writeIndex);
|
||||
}
|
||||
|
||||
public void put(int value) {
|
||||
this.buffer[this.writeIndex] = (byte)value;
|
||||
this.writeIndex = (this.writeIndex + 1) % this.size;
|
||||
}
|
||||
|
||||
public int get() {
|
||||
if (available()) {
|
||||
int value = this.buffer[this.readIndex];
|
||||
this.readIndex = (this.readIndex + 1) % this.size;
|
||||
return value & 0xFF;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void copy(int distance, int length) {
|
||||
int pos1 = this.writeIndex - distance;
|
||||
int pos2 = pos1 + length;
|
||||
for (int i = pos1; i < pos2; i++) {
|
||||
this.buffer[this.writeIndex] = this.buffer[(i + this.size) % this.size];
|
||||
this.writeIndex = (this.writeIndex + 1) % this.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
class ExplodingInputStream extends InputStream {
|
||||
private final InputStream in;
|
||||
|
||||
private BitStream bits;
|
||||
|
||||
private final int dictionarySize;
|
||||
|
||||
private final int numberOfTrees;
|
||||
|
||||
private final int minimumMatchLength;
|
||||
|
||||
private BinaryTree literalTree;
|
||||
|
||||
private BinaryTree lengthTree;
|
||||
|
||||
private BinaryTree distanceTree;
|
||||
|
||||
private final CircularBuffer buffer = new CircularBuffer(32768);
|
||||
|
||||
public ExplodingInputStream(int dictionarySize, int numberOfTrees, InputStream in) {
|
||||
if (dictionarySize != 4096 && dictionarySize != 8192)
|
||||
throw new IllegalArgumentException("The dictionary size must be 4096 or 8192");
|
||||
if (numberOfTrees != 2 && numberOfTrees != 3)
|
||||
throw new IllegalArgumentException("The number of trees must be 2 or 3");
|
||||
this.dictionarySize = dictionarySize;
|
||||
this.numberOfTrees = numberOfTrees;
|
||||
this.minimumMatchLength = numberOfTrees;
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
private void init() throws IOException {
|
||||
if (this.bits == null) {
|
||||
if (this.numberOfTrees == 3)
|
||||
this.literalTree = BinaryTree.decode(this.in, 256);
|
||||
this.lengthTree = BinaryTree.decode(this.in, 64);
|
||||
this.distanceTree = BinaryTree.decode(this.in, 64);
|
||||
this.bits = new BitStream(this.in);
|
||||
}
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
if (!this.buffer.available())
|
||||
fillBuffer();
|
||||
return this.buffer.get();
|
||||
}
|
||||
|
||||
private void fillBuffer() throws IOException {
|
||||
init();
|
||||
int bit = this.bits.nextBit();
|
||||
if (bit == 1) {
|
||||
int literal;
|
||||
if (this.literalTree != null) {
|
||||
literal = this.literalTree.read(this.bits);
|
||||
} else {
|
||||
literal = this.bits.nextBits(8);
|
||||
}
|
||||
if (literal == -1)
|
||||
return;
|
||||
this.buffer.put(literal);
|
||||
} else if (bit == 0) {
|
||||
int distanceLowSize = (this.dictionarySize == 4096) ? 6 : 7;
|
||||
int distanceLow = this.bits.nextBits(distanceLowSize);
|
||||
int distanceHigh = this.distanceTree.read(this.bits);
|
||||
if (distanceHigh == -1 && distanceLow <= 0)
|
||||
return;
|
||||
int distance = distanceHigh << distanceLowSize | distanceLow;
|
||||
int length = this.lengthTree.read(this.bits);
|
||||
if (length == 63)
|
||||
length += this.bits.nextBits(8);
|
||||
length += this.minimumMatchLength;
|
||||
this.buffer.copy(distance + 1, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
public class ExtraFieldUtils {
|
||||
private static final int WORD = 4;
|
||||
|
||||
private static final Map<ZipShort, Class<?>> implementations = new ConcurrentHashMap<ZipShort, Class<?>>();
|
||||
|
||||
static {
|
||||
register(AsiExtraField.class);
|
||||
register(X5455_ExtendedTimestamp.class);
|
||||
register(X7875_NewUnix.class);
|
||||
register(JarMarker.class);
|
||||
register(UnicodePathExtraField.class);
|
||||
register(UnicodeCommentExtraField.class);
|
||||
register(Zip64ExtendedInformationExtraField.class);
|
||||
}
|
||||
|
||||
public static void register(Class<?> c) {
|
||||
try {
|
||||
ZipExtraField ze = (ZipExtraField)c.newInstance();
|
||||
implementations.put(ze.getHeaderId(), c);
|
||||
} catch (ClassCastException cc) {
|
||||
throw new RuntimeException(c + " doesn't implement ZipExtraField");
|
||||
} catch (InstantiationException ie) {
|
||||
throw new RuntimeException(c + " is not a concrete class");
|
||||
} catch (IllegalAccessException ie) {
|
||||
throw new RuntimeException(c + "'s no-arg constructor is not public");
|
||||
}
|
||||
}
|
||||
|
||||
public static ZipExtraField createExtraField(ZipShort headerId) throws InstantiationException, IllegalAccessException {
|
||||
Class<?> c = implementations.get(headerId);
|
||||
if (c != null)
|
||||
return (ZipExtraField)c.newInstance();
|
||||
UnrecognizedExtraField u = new UnrecognizedExtraField();
|
||||
u.setHeaderId(headerId);
|
||||
return u;
|
||||
}
|
||||
|
||||
public static ZipExtraField[] parse(byte[] data) throws ZipException {
|
||||
return parse(data, true, UnparseableExtraField.THROW);
|
||||
}
|
||||
|
||||
public static ZipExtraField[] parse(byte[] data, boolean local) throws ZipException {
|
||||
return parse(data, local, UnparseableExtraField.THROW);
|
||||
}
|
||||
|
||||
public static ZipExtraField[] parse(byte[] data, boolean local, UnparseableExtraField onUnparseableData) throws ZipException {
|
||||
List<ZipExtraField> v = new ArrayList<ZipExtraField>();
|
||||
int start = 0;
|
||||
while (start <= data.length - 4) {
|
||||
ZipShort headerId = new ZipShort(data, start);
|
||||
int length = new ZipShort(data, start + 2).getValue();
|
||||
if (start + 4 + length > data.length) {
|
||||
UnparseableExtraFieldData field;
|
||||
switch (onUnparseableData.getKey()) {
|
||||
case 0:
|
||||
throw new ZipException("bad extra field starting at " + start + ". Block length of " + length + " bytes exceeds remaining" + " data of " + (data.length - start - 4) + " bytes.");
|
||||
case 2:
|
||||
field = new UnparseableExtraFieldData();
|
||||
if (local) {
|
||||
field.parseFromLocalFileData(data, start, data.length - start);
|
||||
} else {
|
||||
field.parseFromCentralDirectoryData(data, start, data.length - start);
|
||||
}
|
||||
v.add(field);
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
}
|
||||
throw new ZipException("unknown UnparseableExtraField key: " + onUnparseableData.getKey());
|
||||
}
|
||||
try {
|
||||
ZipExtraField ze = createExtraField(headerId);
|
||||
if (local) {
|
||||
ze.parseFromLocalFileData(data, start + 4, length);
|
||||
} else {
|
||||
ze.parseFromCentralDirectoryData(data, start + 4, length);
|
||||
}
|
||||
v.add(ze);
|
||||
} catch (InstantiationException ie) {
|
||||
throw (ZipException)new ZipException(ie.getMessage()).initCause(ie);
|
||||
} catch (IllegalAccessException iae) {
|
||||
throw (ZipException)new ZipException(iae.getMessage()).initCause(iae);
|
||||
}
|
||||
start += length + 4;
|
||||
}
|
||||
ZipExtraField[] result = new ZipExtraField[v.size()];
|
||||
return v.<ZipExtraField>toArray(result);
|
||||
}
|
||||
|
||||
public static byte[] mergeLocalFileDataData(ZipExtraField[] data) {
|
||||
boolean lastIsUnparseableHolder = (data.length > 0 && data[data.length - 1] instanceof UnparseableExtraFieldData);
|
||||
int regularExtraFieldCount = lastIsUnparseableHolder ? (data.length - 1) : data.length;
|
||||
int sum = 4 * regularExtraFieldCount;
|
||||
for (ZipExtraField element : data)
|
||||
sum += element.getLocalFileDataLength().getValue();
|
||||
byte[] result = new byte[sum];
|
||||
int start = 0;
|
||||
for (int i = 0; i < regularExtraFieldCount; i++) {
|
||||
System.arraycopy(data[i].getHeaderId().getBytes(), 0, result, start, 2);
|
||||
System.arraycopy(data[i].getLocalFileDataLength().getBytes(), 0, result, start + 2, 2);
|
||||
start += 4;
|
||||
byte[] local = data[i].getLocalFileDataData();
|
||||
if (local != null) {
|
||||
System.arraycopy(local, 0, result, start, local.length);
|
||||
start += local.length;
|
||||
}
|
||||
}
|
||||
if (lastIsUnparseableHolder) {
|
||||
byte[] local = data[data.length - 1].getLocalFileDataData();
|
||||
if (local != null)
|
||||
System.arraycopy(local, 0, result, start, local.length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] mergeCentralDirectoryData(ZipExtraField[] data) {
|
||||
boolean lastIsUnparseableHolder = (data.length > 0 && data[data.length - 1] instanceof UnparseableExtraFieldData);
|
||||
int regularExtraFieldCount = lastIsUnparseableHolder ? (data.length - 1) : data.length;
|
||||
int sum = 4 * regularExtraFieldCount;
|
||||
for (ZipExtraField element : data)
|
||||
sum += element.getCentralDirectoryLength().getValue();
|
||||
byte[] result = new byte[sum];
|
||||
int start = 0;
|
||||
for (int i = 0; i < regularExtraFieldCount; i++) {
|
||||
System.arraycopy(data[i].getHeaderId().getBytes(), 0, result, start, 2);
|
||||
System.arraycopy(data[i].getCentralDirectoryLength().getBytes(), 0, result, start + 2, 2);
|
||||
start += 4;
|
||||
byte[] local = data[i].getCentralDirectoryData();
|
||||
if (local != null) {
|
||||
System.arraycopy(local, 0, result, start, local.length);
|
||||
start += local.length;
|
||||
}
|
||||
}
|
||||
if (lastIsUnparseableHolder) {
|
||||
byte[] local = data[data.length - 1].getCentralDirectoryData();
|
||||
if (local != null)
|
||||
System.arraycopy(local, 0, result, start, local.length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static final class UnparseableExtraField {
|
||||
public static final int THROW_KEY = 0;
|
||||
|
||||
public static final int SKIP_KEY = 1;
|
||||
|
||||
public static final int READ_KEY = 2;
|
||||
|
||||
public static final UnparseableExtraField THROW = new UnparseableExtraField(0);
|
||||
|
||||
public static final UnparseableExtraField SKIP = new UnparseableExtraField(1);
|
||||
|
||||
public static final UnparseableExtraField READ = new UnparseableExtraField(2);
|
||||
|
||||
private final int key;
|
||||
|
||||
private UnparseableExtraField(int k) {
|
||||
this.key = k;
|
||||
}
|
||||
|
||||
public int getKey() {
|
||||
return this.key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
class FallbackZipEncoding implements ZipEncoding {
|
||||
private final String charsetName;
|
||||
|
||||
public FallbackZipEncoding() {
|
||||
this.charsetName = null;
|
||||
}
|
||||
|
||||
public FallbackZipEncoding(String charsetName) {
|
||||
this.charsetName = charsetName;
|
||||
}
|
||||
|
||||
public boolean canEncode(String name) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public ByteBuffer encode(String name) throws IOException {
|
||||
if (this.charsetName == null)
|
||||
return ByteBuffer.wrap(name.getBytes());
|
||||
return ByteBuffer.wrap(name.getBytes(this.charsetName));
|
||||
}
|
||||
|
||||
public String decode(byte[] data) throws IOException {
|
||||
if (this.charsetName == null)
|
||||
return new String(data);
|
||||
return new String(data, this.charsetName);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
public final class GeneralPurposeBit {
|
||||
private static final int ENCRYPTION_FLAG = 1;
|
||||
|
||||
private static final int SLIDING_DICTIONARY_SIZE_FLAG = 2;
|
||||
|
||||
private static final int NUMBER_OF_SHANNON_FANO_TREES_FLAG = 4;
|
||||
|
||||
private static final int DATA_DESCRIPTOR_FLAG = 8;
|
||||
|
||||
private static final int STRONG_ENCRYPTION_FLAG = 64;
|
||||
|
||||
public static final int UFT8_NAMES_FLAG = 2048;
|
||||
|
||||
private boolean languageEncodingFlag = false;
|
||||
|
||||
private boolean dataDescriptorFlag = false;
|
||||
|
||||
private boolean encryptionFlag = false;
|
||||
|
||||
private boolean strongEncryptionFlag = false;
|
||||
|
||||
private int slidingDictionarySize;
|
||||
|
||||
private int numberOfShannonFanoTrees;
|
||||
|
||||
public boolean usesUTF8ForNames() {
|
||||
return this.languageEncodingFlag;
|
||||
}
|
||||
|
||||
public void useUTF8ForNames(boolean b) {
|
||||
this.languageEncodingFlag = b;
|
||||
}
|
||||
|
||||
public boolean usesDataDescriptor() {
|
||||
return this.dataDescriptorFlag;
|
||||
}
|
||||
|
||||
public void useDataDescriptor(boolean b) {
|
||||
this.dataDescriptorFlag = b;
|
||||
}
|
||||
|
||||
public boolean usesEncryption() {
|
||||
return this.encryptionFlag;
|
||||
}
|
||||
|
||||
public void useEncryption(boolean b) {
|
||||
this.encryptionFlag = b;
|
||||
}
|
||||
|
||||
public boolean usesStrongEncryption() {
|
||||
return (this.encryptionFlag && this.strongEncryptionFlag);
|
||||
}
|
||||
|
||||
public void useStrongEncryption(boolean b) {
|
||||
this.strongEncryptionFlag = b;
|
||||
if (b)
|
||||
useEncryption(true);
|
||||
}
|
||||
|
||||
int getSlidingDictionarySize() {
|
||||
return this.slidingDictionarySize;
|
||||
}
|
||||
|
||||
int getNumberOfShannonFanoTrees() {
|
||||
return this.numberOfShannonFanoTrees;
|
||||
}
|
||||
|
||||
public byte[] encode() {
|
||||
return ZipShort.getBytes((this.dataDescriptorFlag ? 8 : 0) | (this.languageEncodingFlag ? 2048 : 0) | (this.encryptionFlag ? 1 : 0) | (this.strongEncryptionFlag ? 64 : 0));
|
||||
}
|
||||
|
||||
public static GeneralPurposeBit parse(byte[] data, int offset) {
|
||||
int generalPurposeFlag = ZipShort.getValue(data, offset);
|
||||
GeneralPurposeBit b = new GeneralPurposeBit();
|
||||
b.useDataDescriptor(((generalPurposeFlag & 0x8) != 0));
|
||||
b.useUTF8ForNames(((generalPurposeFlag & 0x800) != 0));
|
||||
b.useStrongEncryption(((generalPurposeFlag & 0x40) != 0));
|
||||
b.useEncryption(((generalPurposeFlag & 0x1) != 0));
|
||||
b.slidingDictionarySize = ((generalPurposeFlag & 0x2) != 0) ? 8192 : 4096;
|
||||
b.numberOfShannonFanoTrees = ((generalPurposeFlag & 0x4) != 0) ? 3 : 2;
|
||||
return b;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return 3 * (7 * (13 * (17 * (this.encryptionFlag ? 1 : 0) + (this.strongEncryptionFlag ? 1 : 0)) + (this.languageEncodingFlag ? 1 : 0)) + (this.dataDescriptorFlag ? 1 : 0));
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof GeneralPurposeBit))
|
||||
return false;
|
||||
GeneralPurposeBit g = (GeneralPurposeBit)o;
|
||||
return (g.encryptionFlag == this.encryptionFlag && g.strongEncryptionFlag == this.strongEncryptionFlag && g.languageEncodingFlag == this.languageEncodingFlag && g.dataDescriptorFlag == this.dataDescriptorFlag);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
public final class JarMarker implements ZipExtraField {
|
||||
private static final ZipShort ID = new ZipShort(51966);
|
||||
|
||||
private static final ZipShort NULL = new ZipShort(0);
|
||||
|
||||
private static final byte[] NO_BYTES = new byte[0];
|
||||
|
||||
private static final JarMarker DEFAULT = new JarMarker();
|
||||
|
||||
public static JarMarker getInstance() {
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
public ZipShort getHeaderId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
public ZipShort getLocalFileDataLength() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public ZipShort getCentralDirectoryLength() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public byte[] getLocalFileDataData() {
|
||||
return NO_BYTES;
|
||||
}
|
||||
|
||||
public byte[] getCentralDirectoryData() {
|
||||
return NO_BYTES;
|
||||
}
|
||||
|
||||
public void parseFromLocalFileData(byte[] data, int offset, int length) throws ZipException {
|
||||
if (length != 0)
|
||||
throw new ZipException("JarMarker doesn't expect any data");
|
||||
}
|
||||
|
||||
public void parseFromCentralDirectoryData(byte[] buffer, int offset, int length) throws ZipException {
|
||||
parseFromLocalFileData(buffer, offset, length);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import java.nio.charset.CoderResult;
|
||||
import java.nio.charset.CodingErrorAction;
|
||||
|
||||
class NioZipEncoding implements ZipEncoding {
|
||||
private final Charset charset;
|
||||
|
||||
public NioZipEncoding(Charset charset) {
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
public boolean canEncode(String name) {
|
||||
CharsetEncoder enc = this.charset.newEncoder();
|
||||
enc.onMalformedInput(CodingErrorAction.REPORT);
|
||||
enc.onUnmappableCharacter(CodingErrorAction.REPORT);
|
||||
return enc.canEncode(name);
|
||||
}
|
||||
|
||||
public ByteBuffer encode(String name) {
|
||||
CharsetEncoder enc = this.charset.newEncoder();
|
||||
enc.onMalformedInput(CodingErrorAction.REPORT);
|
||||
enc.onUnmappableCharacter(CodingErrorAction.REPORT);
|
||||
CharBuffer cb = CharBuffer.wrap(name);
|
||||
ByteBuffer out = ByteBuffer.allocate(name.length() + (name.length() + 1) / 2);
|
||||
while (cb.remaining() > 0) {
|
||||
CoderResult res = enc.encode(cb, out, true);
|
||||
if (res.isUnmappable() || res.isMalformed()) {
|
||||
if (res.length() * 6 > out.remaining())
|
||||
out = ZipEncodingHelper.growBuffer(out, out.position() + res.length() * 6);
|
||||
for (int i = 0; i < res.length(); i++)
|
||||
ZipEncodingHelper.appendSurrogate(out, cb.get());
|
||||
continue;
|
||||
}
|
||||
if (res.isOverflow()) {
|
||||
out = ZipEncodingHelper.growBuffer(out, 0);
|
||||
continue;
|
||||
}
|
||||
if (res.isUnderflow()) {
|
||||
enc.flush(out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
out.limit(out.position());
|
||||
out.rewind();
|
||||
return out;
|
||||
}
|
||||
|
||||
public String decode(byte[] data) throws IOException {
|
||||
return this.charset.newDecoder().onMalformedInput(CodingErrorAction.REPORT).onUnmappableCharacter(CodingErrorAction.REPORT).decode(ByteBuffer.wrap(data)).toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
class Simple8BitZipEncoding implements ZipEncoding {
|
||||
private final char[] highChars;
|
||||
|
||||
private final List<Simple8BitChar> reverseMapping;
|
||||
|
||||
private static final class Simple8BitChar implements Comparable<Simple8BitChar> {
|
||||
public final char unicode;
|
||||
|
||||
public final byte code;
|
||||
|
||||
Simple8BitChar(byte code, char unicode) {
|
||||
this.code = code;
|
||||
this.unicode = unicode;
|
||||
}
|
||||
|
||||
public int compareTo(Simple8BitChar a) {
|
||||
return this.unicode - a.unicode;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "0x" + Integer.toHexString(Character.MAX_VALUE & this.unicode) + "->0x" + Integer.toHexString(0xFF & this.code);
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof Simple8BitChar) {
|
||||
Simple8BitChar other = (Simple8BitChar)o;
|
||||
return (this.unicode == other.unicode && this.code == other.code);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return this.unicode;
|
||||
}
|
||||
}
|
||||
|
||||
public Simple8BitZipEncoding(char[] highChars) {
|
||||
this.highChars = (char[])highChars.clone();
|
||||
List<Simple8BitChar> temp = new ArrayList<Simple8BitChar>(this.highChars.length);
|
||||
byte code = Byte.MAX_VALUE;
|
||||
for (char highChar : this.highChars) {
|
||||
code = (byte)(code + 1);
|
||||
temp.add(new Simple8BitChar(code, highChar));
|
||||
}
|
||||
Collections.<Simple8BitChar>sort(temp);
|
||||
this.reverseMapping = Collections.<Simple8BitChar>unmodifiableList(temp);
|
||||
}
|
||||
|
||||
public char decodeByte(byte b) {
|
||||
if (b >= 0)
|
||||
return (char)b;
|
||||
return this.highChars[128 + b];
|
||||
}
|
||||
|
||||
public boolean canEncodeChar(char c) {
|
||||
if (c >= '\000' && c < '\u0080')
|
||||
return true;
|
||||
Simple8BitChar r = encodeHighChar(c);
|
||||
return (r != null);
|
||||
}
|
||||
|
||||
public boolean pushEncodedChar(ByteBuffer bb, char c) {
|
||||
if (c >= '\000' && c < '\u0080') {
|
||||
bb.put((byte)c);
|
||||
return true;
|
||||
}
|
||||
Simple8BitChar r = encodeHighChar(c);
|
||||
if (r == null)
|
||||
return false;
|
||||
bb.put(r.code);
|
||||
return true;
|
||||
}
|
||||
|
||||
private Simple8BitChar encodeHighChar(char c) {
|
||||
int i0 = 0;
|
||||
int i1 = this.reverseMapping.size();
|
||||
while (i1 > i0) {
|
||||
int i = i0 + (i1 - i0) / 2;
|
||||
Simple8BitChar m = this.reverseMapping.get(i);
|
||||
if (m.unicode == c)
|
||||
return m;
|
||||
if (m.unicode < c) {
|
||||
i0 = i + 1;
|
||||
continue;
|
||||
}
|
||||
i1 = i;
|
||||
}
|
||||
if (i0 >= this.reverseMapping.size())
|
||||
return null;
|
||||
Simple8BitChar r = this.reverseMapping.get(i0);
|
||||
if (r.unicode != c)
|
||||
return null;
|
||||
return r;
|
||||
}
|
||||
|
||||
public boolean canEncode(String name) {
|
||||
for (int i = 0; i < name.length(); i++) {
|
||||
char c = name.charAt(i);
|
||||
if (!canEncodeChar(c))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public ByteBuffer encode(String name) {
|
||||
ByteBuffer out = ByteBuffer.allocate(name.length() + 6 + (name.length() + 1) / 2);
|
||||
for (int i = 0; i < name.length(); i++) {
|
||||
char c = name.charAt(i);
|
||||
if (out.remaining() < 6)
|
||||
out = ZipEncodingHelper.growBuffer(out, out.position() + 6);
|
||||
if (!pushEncodedChar(out, c))
|
||||
ZipEncodingHelper.appendSurrogate(out, c);
|
||||
}
|
||||
out.limit(out.position());
|
||||
out.rewind();
|
||||
return out;
|
||||
}
|
||||
|
||||
public String decode(byte[] data) throws IOException {
|
||||
char[] ret = new char[data.length];
|
||||
for (int i = 0; i < data.length; i++)
|
||||
ret[i] = decodeByte(data[i]);
|
||||
return new String(ret);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
public class UnicodeCommentExtraField extends AbstractUnicodeExtraField {
|
||||
public static final ZipShort UCOM_ID = new ZipShort(25461);
|
||||
|
||||
public UnicodeCommentExtraField() {}
|
||||
|
||||
public UnicodeCommentExtraField(String text, byte[] bytes, int off, int len) {
|
||||
super(text, bytes, off, len);
|
||||
}
|
||||
|
||||
public UnicodeCommentExtraField(String comment, byte[] bytes) {
|
||||
super(comment, bytes);
|
||||
}
|
||||
|
||||
public ZipShort getHeaderId() {
|
||||
return UCOM_ID;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
public class UnicodePathExtraField extends AbstractUnicodeExtraField {
|
||||
public static final ZipShort UPATH_ID = new ZipShort(28789);
|
||||
|
||||
public UnicodePathExtraField() {}
|
||||
|
||||
public UnicodePathExtraField(String text, byte[] bytes, int off, int len) {
|
||||
super(text, bytes, off, len);
|
||||
}
|
||||
|
||||
public UnicodePathExtraField(String name, byte[] bytes) {
|
||||
super(name, bytes);
|
||||
}
|
||||
|
||||
public ZipShort getHeaderId() {
|
||||
return UPATH_ID;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
public interface UnixStat {
|
||||
public static final int PERM_MASK = 4095;
|
||||
|
||||
public static final int LINK_FLAG = 40960;
|
||||
|
||||
public static final int FILE_FLAG = 32768;
|
||||
|
||||
public static final int DIR_FLAG = 16384;
|
||||
|
||||
public static final int DEFAULT_LINK_PERM = 511;
|
||||
|
||||
public static final int DEFAULT_DIR_PERM = 493;
|
||||
|
||||
public static final int DEFAULT_FILE_PERM = 420;
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
public final class UnparseableExtraFieldData implements ZipExtraField {
|
||||
private static final ZipShort HEADER_ID = new ZipShort(44225);
|
||||
|
||||
private byte[] localFileData;
|
||||
|
||||
private byte[] centralDirectoryData;
|
||||
|
||||
public ZipShort getHeaderId() {
|
||||
return HEADER_ID;
|
||||
}
|
||||
|
||||
public ZipShort getLocalFileDataLength() {
|
||||
return new ZipShort((this.localFileData == null) ? 0 : this.localFileData.length);
|
||||
}
|
||||
|
||||
public ZipShort getCentralDirectoryLength() {
|
||||
return (this.centralDirectoryData == null) ? getLocalFileDataLength() : new ZipShort(this.centralDirectoryData.length);
|
||||
}
|
||||
|
||||
public byte[] getLocalFileDataData() {
|
||||
return ZipUtil.copy(this.localFileData);
|
||||
}
|
||||
|
||||
public byte[] getCentralDirectoryData() {
|
||||
return (this.centralDirectoryData == null) ? getLocalFileDataData() : ZipUtil.copy(this.centralDirectoryData);
|
||||
}
|
||||
|
||||
public void parseFromLocalFileData(byte[] buffer, int offset, int length) {
|
||||
this.localFileData = new byte[length];
|
||||
System.arraycopy(buffer, offset, this.localFileData, 0, length);
|
||||
}
|
||||
|
||||
public void parseFromCentralDirectoryData(byte[] buffer, int offset, int length) {
|
||||
this.centralDirectoryData = new byte[length];
|
||||
System.arraycopy(buffer, offset, this.centralDirectoryData, 0, length);
|
||||
if (this.localFileData == null)
|
||||
parseFromLocalFileData(buffer, offset, length);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
public class UnrecognizedExtraField implements ZipExtraField {
|
||||
private ZipShort headerId;
|
||||
|
||||
private byte[] localData;
|
||||
|
||||
private byte[] centralData;
|
||||
|
||||
public void setHeaderId(ZipShort headerId) {
|
||||
this.headerId = headerId;
|
||||
}
|
||||
|
||||
public ZipShort getHeaderId() {
|
||||
return this.headerId;
|
||||
}
|
||||
|
||||
public void setLocalFileDataData(byte[] data) {
|
||||
this.localData = ZipUtil.copy(data);
|
||||
}
|
||||
|
||||
public ZipShort getLocalFileDataLength() {
|
||||
return new ZipShort((this.localData != null) ? this.localData.length : 0);
|
||||
}
|
||||
|
||||
public byte[] getLocalFileDataData() {
|
||||
return ZipUtil.copy(this.localData);
|
||||
}
|
||||
|
||||
public void setCentralDirectoryData(byte[] data) {
|
||||
this.centralData = ZipUtil.copy(data);
|
||||
}
|
||||
|
||||
public ZipShort getCentralDirectoryLength() {
|
||||
if (this.centralData != null)
|
||||
return new ZipShort(this.centralData.length);
|
||||
return getLocalFileDataLength();
|
||||
}
|
||||
|
||||
public byte[] getCentralDirectoryData() {
|
||||
if (this.centralData != null)
|
||||
return ZipUtil.copy(this.centralData);
|
||||
return getLocalFileDataData();
|
||||
}
|
||||
|
||||
public void parseFromLocalFileData(byte[] data, int offset, int length) {
|
||||
byte[] tmp = new byte[length];
|
||||
System.arraycopy(data, offset, tmp, 0, length);
|
||||
setLocalFileDataData(tmp);
|
||||
}
|
||||
|
||||
public void parseFromCentralDirectoryData(byte[] data, int offset, int length) {
|
||||
byte[] tmp = new byte[length];
|
||||
System.arraycopy(data, offset, tmp, 0, length);
|
||||
setCentralDirectoryData(tmp);
|
||||
if (this.localData == null)
|
||||
setLocalFileDataData(tmp);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import org.apache.commons.compress.compressors.z._internal_.InternalLZWInputStream;
|
||||
|
||||
class UnshrinkingInputStream extends InternalLZWInputStream {
|
||||
private static final int MAX_CODE_SIZE = 13;
|
||||
|
||||
private static final int MAX_TABLE_SIZE = 8192;
|
||||
|
||||
private final boolean[] isUsed;
|
||||
|
||||
public UnshrinkingInputStream(InputStream inputStream) throws IOException {
|
||||
super(inputStream);
|
||||
setClearCode(this.codeSize);
|
||||
initializeTables(13);
|
||||
this.isUsed = new boolean[this.prefixes.length];
|
||||
for (int i = 0; i < 256; i++)
|
||||
this.isUsed[i] = true;
|
||||
this.tableSize = this.clearCode + 1;
|
||||
}
|
||||
|
||||
protected int addEntry(int previousCode, byte character) throws IOException {
|
||||
while (this.tableSize < 8192 && this.isUsed[this.tableSize])
|
||||
this.tableSize++;
|
||||
int idx = addEntry(previousCode, character, 8192);
|
||||
if (idx >= 0)
|
||||
this.isUsed[idx] = true;
|
||||
return idx;
|
||||
}
|
||||
|
||||
private void partialClear() {
|
||||
boolean[] isParent = new boolean[8192];
|
||||
for (int j = 0; j < this.isUsed.length; j++) {
|
||||
if (this.isUsed[j] && this.prefixes[j] != -1)
|
||||
isParent[this.prefixes[j]] = true;
|
||||
}
|
||||
for (int i = this.clearCode + 1; i < isParent.length; i++) {
|
||||
if (!isParent[i]) {
|
||||
this.isUsed[i] = false;
|
||||
this.prefixes[i] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected int decompressNextSymbol() throws IOException {
|
||||
int code = readNextCode();
|
||||
if (code < 0)
|
||||
return -1;
|
||||
if (code == this.clearCode) {
|
||||
int subCode = readNextCode();
|
||||
if (subCode < 0)
|
||||
throw new IOException("Unexpected EOF;");
|
||||
if (subCode == 1) {
|
||||
if (this.codeSize < 13) {
|
||||
this.codeSize++;
|
||||
} else {
|
||||
throw new IOException("Attempt to increase code size beyond maximum");
|
||||
}
|
||||
} else if (subCode == 2) {
|
||||
partialClear();
|
||||
this.tableSize = this.clearCode + 1;
|
||||
} else {
|
||||
throw new IOException("Invalid clear code subcode " + subCode);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
boolean addedUnfinishedEntry = false;
|
||||
int effectiveCode = code;
|
||||
if (!this.isUsed[code]) {
|
||||
effectiveCode = addRepeatOfPreviousCode();
|
||||
addedUnfinishedEntry = true;
|
||||
}
|
||||
return expandCodeToOutputStack(effectiveCode, addedUnfinishedEntry);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
public class UnsupportedZipFeatureException extends ZipException {
|
||||
private final Feature reason;
|
||||
|
||||
private final ZipArchiveEntry entry;
|
||||
|
||||
private static final long serialVersionUID = 20130101L;
|
||||
|
||||
public UnsupportedZipFeatureException(Feature reason, ZipArchiveEntry entry) {
|
||||
super("unsupported feature " + reason + " used in entry " + entry.getName());
|
||||
this.reason = reason;
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
public UnsupportedZipFeatureException(ZipMethod method, ZipArchiveEntry entry) {
|
||||
super("unsupported feature method '" + method.name() + "' used in entry " + entry.getName());
|
||||
this.reason = Feature.METHOD;
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
public UnsupportedZipFeatureException(Feature reason) {
|
||||
super("unsupported feature " + reason + " used in archive.");
|
||||
this.reason = reason;
|
||||
this.entry = null;
|
||||
}
|
||||
|
||||
public Feature getFeature() {
|
||||
return this.reason;
|
||||
}
|
||||
|
||||
public ZipArchiveEntry getEntry() {
|
||||
return this.entry;
|
||||
}
|
||||
|
||||
public static class Feature {
|
||||
public static final Feature ENCRYPTION = new Feature("encryption");
|
||||
|
||||
public static final Feature METHOD = new Feature("compression method");
|
||||
|
||||
public static final Feature DATA_DESCRIPTOR = new Feature("data descriptor");
|
||||
|
||||
public static final Feature SPLITTING = new Feature("splitting");
|
||||
|
||||
private final String name;
|
||||
|
||||
private Feature(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,231 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
public class X5455_ExtendedTimestamp implements ZipExtraField, Cloneable, Serializable {
|
||||
private static final ZipShort HEADER_ID = new ZipShort(21589);
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final byte MODIFY_TIME_BIT = 1;
|
||||
|
||||
public static final byte ACCESS_TIME_BIT = 2;
|
||||
|
||||
public static final byte CREATE_TIME_BIT = 4;
|
||||
|
||||
private byte flags;
|
||||
|
||||
private boolean bit0_modifyTimePresent;
|
||||
|
||||
private boolean bit1_accessTimePresent;
|
||||
|
||||
private boolean bit2_createTimePresent;
|
||||
|
||||
private ZipLong modifyTime;
|
||||
|
||||
private ZipLong accessTime;
|
||||
|
||||
private ZipLong createTime;
|
||||
|
||||
public ZipShort getHeaderId() {
|
||||
return HEADER_ID;
|
||||
}
|
||||
|
||||
public ZipShort getLocalFileDataLength() {
|
||||
return new ZipShort(1 + (this.bit0_modifyTimePresent ? 4 : 0) + ((this.bit1_accessTimePresent && this.accessTime != null) ? 4 : 0) + ((this.bit2_createTimePresent && this.createTime != null) ? 4 : 0));
|
||||
}
|
||||
|
||||
public ZipShort getCentralDirectoryLength() {
|
||||
return new ZipShort(1 + (this.bit0_modifyTimePresent ? 4 : 0));
|
||||
}
|
||||
|
||||
public byte[] getLocalFileDataData() {
|
||||
byte[] data = new byte[getLocalFileDataLength().getValue()];
|
||||
int pos = 0;
|
||||
data[pos++] = 0;
|
||||
if (this.bit0_modifyTimePresent) {
|
||||
data[0] = (byte)(data[0] | 0x1);
|
||||
System.arraycopy(this.modifyTime.getBytes(), 0, data, pos, 4);
|
||||
pos += 4;
|
||||
}
|
||||
if (this.bit1_accessTimePresent && this.accessTime != null) {
|
||||
data[0] = (byte)(data[0] | 0x2);
|
||||
System.arraycopy(this.accessTime.getBytes(), 0, data, pos, 4);
|
||||
pos += 4;
|
||||
}
|
||||
if (this.bit2_createTimePresent && this.createTime != null) {
|
||||
data[0] = (byte)(data[0] | 0x4);
|
||||
System.arraycopy(this.createTime.getBytes(), 0, data, pos, 4);
|
||||
pos += 4;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public byte[] getCentralDirectoryData() {
|
||||
byte[] centralData = new byte[getCentralDirectoryLength().getValue()];
|
||||
byte[] localData = getLocalFileDataData();
|
||||
System.arraycopy(localData, 0, centralData, 0, centralData.length);
|
||||
return centralData;
|
||||
}
|
||||
|
||||
public void parseFromLocalFileData(byte[] data, int offset, int length) throws ZipException {
|
||||
reset();
|
||||
int len = offset + length;
|
||||
setFlags(data[offset++]);
|
||||
if (this.bit0_modifyTimePresent) {
|
||||
this.modifyTime = new ZipLong(data, offset);
|
||||
offset += 4;
|
||||
}
|
||||
if (this.bit1_accessTimePresent && offset + 4 <= len) {
|
||||
this.accessTime = new ZipLong(data, offset);
|
||||
offset += 4;
|
||||
}
|
||||
if (this.bit2_createTimePresent && offset + 4 <= len) {
|
||||
this.createTime = new ZipLong(data, offset);
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
|
||||
public void parseFromCentralDirectoryData(byte[] buffer, int offset, int length) throws ZipException {
|
||||
reset();
|
||||
parseFromLocalFileData(buffer, offset, length);
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
setFlags((byte)0);
|
||||
this.modifyTime = null;
|
||||
this.accessTime = null;
|
||||
this.createTime = null;
|
||||
}
|
||||
|
||||
public void setFlags(byte flags) {
|
||||
this.flags = flags;
|
||||
this.bit0_modifyTimePresent = ((flags & 0x1) == 1);
|
||||
this.bit1_accessTimePresent = ((flags & 0x2) == 2);
|
||||
this.bit2_createTimePresent = ((flags & 0x4) == 4);
|
||||
}
|
||||
|
||||
public byte getFlags() {
|
||||
return this.flags;
|
||||
}
|
||||
|
||||
public boolean isBit0_modifyTimePresent() {
|
||||
return this.bit0_modifyTimePresent;
|
||||
}
|
||||
|
||||
public boolean isBit1_accessTimePresent() {
|
||||
return this.bit1_accessTimePresent;
|
||||
}
|
||||
|
||||
public boolean isBit2_createTimePresent() {
|
||||
return this.bit2_createTimePresent;
|
||||
}
|
||||
|
||||
public ZipLong getModifyTime() {
|
||||
return this.modifyTime;
|
||||
}
|
||||
|
||||
public ZipLong getAccessTime() {
|
||||
return this.accessTime;
|
||||
}
|
||||
|
||||
public ZipLong getCreateTime() {
|
||||
return this.createTime;
|
||||
}
|
||||
|
||||
public Date getModifyJavaTime() {
|
||||
return (this.modifyTime != null) ? new Date(this.modifyTime.getValue() * 1000L) : null;
|
||||
}
|
||||
|
||||
public Date getAccessJavaTime() {
|
||||
return (this.accessTime != null) ? new Date(this.accessTime.getValue() * 1000L) : null;
|
||||
}
|
||||
|
||||
public Date getCreateJavaTime() {
|
||||
return (this.createTime != null) ? new Date(this.createTime.getValue() * 1000L) : null;
|
||||
}
|
||||
|
||||
public void setModifyTime(ZipLong l) {
|
||||
this.bit0_modifyTimePresent = (l != null);
|
||||
this.flags = (byte)((l != null) ? (this.flags | 0x1) : (this.flags & 0xFFFFFFFE));
|
||||
this.modifyTime = l;
|
||||
}
|
||||
|
||||
public void setAccessTime(ZipLong l) {
|
||||
this.bit1_accessTimePresent = (l != null);
|
||||
this.flags = (byte)((l != null) ? (this.flags | 0x2) : (this.flags & 0xFFFFFFFD));
|
||||
this.accessTime = l;
|
||||
}
|
||||
|
||||
public void setCreateTime(ZipLong l) {
|
||||
this.bit2_createTimePresent = (l != null);
|
||||
this.flags = (byte)((l != null) ? (this.flags | 0x4) : (this.flags & 0xFFFFFFFB));
|
||||
this.createTime = l;
|
||||
}
|
||||
|
||||
public void setModifyJavaTime(Date d) {
|
||||
setModifyTime(dateToZipLong(d));
|
||||
}
|
||||
|
||||
public void setAccessJavaTime(Date d) {
|
||||
setAccessTime(dateToZipLong(d));
|
||||
}
|
||||
|
||||
public void setCreateJavaTime(Date d) {
|
||||
setCreateTime(dateToZipLong(d));
|
||||
}
|
||||
|
||||
private static ZipLong dateToZipLong(Date d) {
|
||||
if (d == null)
|
||||
return null;
|
||||
long TWO_TO_32 = 4294967296L;
|
||||
long l = d.getTime() / 1000L;
|
||||
if (l >= 4294967296L)
|
||||
throw new IllegalArgumentException("Cannot set an X5455 timestamp larger than 2^32: " + l);
|
||||
return new ZipLong(l);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("0x5455 Zip Extra Field: Flags=");
|
||||
buf.append(Integer.toBinaryString(ZipUtil.unsignedIntToSignedByte(this.flags))).append(" ");
|
||||
if (this.bit0_modifyTimePresent && this.modifyTime != null) {
|
||||
Date m = getModifyJavaTime();
|
||||
buf.append(" Modify:[").append(m).append("] ");
|
||||
}
|
||||
if (this.bit1_accessTimePresent && this.accessTime != null) {
|
||||
Date a = getAccessJavaTime();
|
||||
buf.append(" Access:[").append(a).append("] ");
|
||||
}
|
||||
if (this.bit2_createTimePresent && this.createTime != null) {
|
||||
Date c = getCreateJavaTime();
|
||||
buf.append(" Create:[").append(c).append("] ");
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof X5455_ExtendedTimestamp) {
|
||||
X5455_ExtendedTimestamp xf = (X5455_ExtendedTimestamp)o;
|
||||
return ((this.flags & 0x7) == (xf.flags & 0x7) && (this.modifyTime == xf.modifyTime || (this.modifyTime != null && this.modifyTime.equals(xf.modifyTime))) && (this.accessTime == xf.accessTime || (this.accessTime != null && this.accessTime.equals(xf.accessTime))) && (this.createTime == xf.createTime || (this.createTime != null && this.createTime.equals(xf.createTime))));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = -123 * (this.flags & 0x7);
|
||||
if (this.modifyTime != null)
|
||||
hc ^= this.modifyTime.hashCode();
|
||||
if (this.accessTime != null)
|
||||
hc ^= Integer.rotateLeft(this.accessTime.hashCode(), 11);
|
||||
if (this.createTime != null)
|
||||
hc ^= Integer.rotateLeft(this.createTime.hashCode(), 22);
|
||||
return hc;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
public class X7875_NewUnix implements ZipExtraField, Cloneable, Serializable {
|
||||
private static final ZipShort HEADER_ID = new ZipShort(30837);
|
||||
|
||||
private static final BigInteger ONE_THOUSAND = BigInteger.valueOf(1000L);
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int version = 1;
|
||||
|
||||
private BigInteger uid;
|
||||
|
||||
private BigInteger gid;
|
||||
|
||||
public X7875_NewUnix() {
|
||||
reset();
|
||||
}
|
||||
|
||||
public ZipShort getHeaderId() {
|
||||
return HEADER_ID;
|
||||
}
|
||||
|
||||
public long getUID() {
|
||||
return ZipUtil.bigToLong(this.uid);
|
||||
}
|
||||
|
||||
public long getGID() {
|
||||
return ZipUtil.bigToLong(this.gid);
|
||||
}
|
||||
|
||||
public void setUID(long l) {
|
||||
this.uid = ZipUtil.longToBig(l);
|
||||
}
|
||||
|
||||
public void setGID(long l) {
|
||||
this.gid = ZipUtil.longToBig(l);
|
||||
}
|
||||
|
||||
public ZipShort getLocalFileDataLength() {
|
||||
int uidSize = (trimLeadingZeroesForceMinLength(this.uid.toByteArray())).length;
|
||||
int gidSize = (trimLeadingZeroesForceMinLength(this.gid.toByteArray())).length;
|
||||
return new ZipShort(3 + uidSize + gidSize);
|
||||
}
|
||||
|
||||
public ZipShort getCentralDirectoryLength() {
|
||||
return getLocalFileDataLength();
|
||||
}
|
||||
|
||||
public byte[] getLocalFileDataData() {
|
||||
byte[] uidBytes = this.uid.toByteArray();
|
||||
byte[] gidBytes = this.gid.toByteArray();
|
||||
uidBytes = trimLeadingZeroesForceMinLength(uidBytes);
|
||||
gidBytes = trimLeadingZeroesForceMinLength(gidBytes);
|
||||
byte[] data = new byte[3 + uidBytes.length + gidBytes.length];
|
||||
ZipUtil.reverse(uidBytes);
|
||||
ZipUtil.reverse(gidBytes);
|
||||
int pos = 0;
|
||||
data[pos++] = ZipUtil.unsignedIntToSignedByte(this.version);
|
||||
data[pos++] = ZipUtil.unsignedIntToSignedByte(uidBytes.length);
|
||||
System.arraycopy(uidBytes, 0, data, pos, uidBytes.length);
|
||||
pos += uidBytes.length;
|
||||
data[pos++] = ZipUtil.unsignedIntToSignedByte(gidBytes.length);
|
||||
System.arraycopy(gidBytes, 0, data, pos, gidBytes.length);
|
||||
return data;
|
||||
}
|
||||
|
||||
public byte[] getCentralDirectoryData() {
|
||||
return getLocalFileDataData();
|
||||
}
|
||||
|
||||
public void parseFromLocalFileData(byte[] data, int offset, int length) throws ZipException {
|
||||
reset();
|
||||
this.version = ZipUtil.signedByteToUnsignedInt(data[offset++]);
|
||||
int uidSize = ZipUtil.signedByteToUnsignedInt(data[offset++]);
|
||||
byte[] uidBytes = new byte[uidSize];
|
||||
System.arraycopy(data, offset, uidBytes, 0, uidSize);
|
||||
offset += uidSize;
|
||||
this.uid = new BigInteger(1, ZipUtil.reverse(uidBytes));
|
||||
int gidSize = ZipUtil.signedByteToUnsignedInt(data[offset++]);
|
||||
byte[] gidBytes = new byte[gidSize];
|
||||
System.arraycopy(data, offset, gidBytes, 0, gidSize);
|
||||
this.gid = new BigInteger(1, ZipUtil.reverse(gidBytes));
|
||||
}
|
||||
|
||||
public void parseFromCentralDirectoryData(byte[] buffer, int offset, int length) throws ZipException {
|
||||
reset();
|
||||
parseFromLocalFileData(buffer, offset, length);
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
this.uid = ONE_THOUSAND;
|
||||
this.gid = ONE_THOUSAND;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "0x7875 Zip Extra Field: UID=" + this.uid + " GID=" + this.gid;
|
||||
}
|
||||
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof X7875_NewUnix) {
|
||||
X7875_NewUnix xf = (X7875_NewUnix)o;
|
||||
return (this.version == xf.version && this.uid.equals(xf.uid) && this.gid.equals(xf.gid));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hc = -1234567 * this.version;
|
||||
hc ^= Integer.rotateLeft(this.uid.hashCode(), 16);
|
||||
hc ^= this.gid.hashCode();
|
||||
return hc;
|
||||
}
|
||||
|
||||
static byte[] trimLeadingZeroesForceMinLength(byte[] array) {
|
||||
if (array == null)
|
||||
return array;
|
||||
int pos = 0;
|
||||
for (byte b : array) {
|
||||
if (b == 0) {
|
||||
pos++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
int MIN_LENGTH = 1;
|
||||
byte[] trimmedArray = new byte[Math.max(1, array.length - pos)];
|
||||
int startPos = trimmedArray.length - (array.length - pos);
|
||||
System.arraycopy(array, pos, trimmedArray, startPos, trimmedArray.length - startPos);
|
||||
return trimmedArray;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
public class Zip64ExtendedInformationExtraField implements ZipExtraField {
|
||||
static final ZipShort HEADER_ID = new ZipShort(1);
|
||||
|
||||
private static final String LFH_MUST_HAVE_BOTH_SIZES_MSG = "Zip64 extended information must contain both size values in the local file header.";
|
||||
|
||||
private static final byte[] EMPTY = new byte[0];
|
||||
|
||||
private ZipEightByteInteger size;
|
||||
|
||||
private ZipEightByteInteger compressedSize;
|
||||
|
||||
private ZipEightByteInteger relativeHeaderOffset;
|
||||
|
||||
private ZipLong diskStart;
|
||||
|
||||
private byte[] rawCentralDirectoryData;
|
||||
|
||||
public Zip64ExtendedInformationExtraField() {}
|
||||
|
||||
public Zip64ExtendedInformationExtraField(ZipEightByteInteger size, ZipEightByteInteger compressedSize) {
|
||||
this(size, compressedSize, null, null);
|
||||
}
|
||||
|
||||
public Zip64ExtendedInformationExtraField(ZipEightByteInteger size, ZipEightByteInteger compressedSize, ZipEightByteInteger relativeHeaderOffset, ZipLong diskStart) {
|
||||
this.size = size;
|
||||
this.compressedSize = compressedSize;
|
||||
this.relativeHeaderOffset = relativeHeaderOffset;
|
||||
this.diskStart = diskStart;
|
||||
}
|
||||
|
||||
public ZipShort getHeaderId() {
|
||||
return HEADER_ID;
|
||||
}
|
||||
|
||||
public ZipShort getLocalFileDataLength() {
|
||||
return new ZipShort((this.size != null) ? 16 : 0);
|
||||
}
|
||||
|
||||
public ZipShort getCentralDirectoryLength() {
|
||||
return new ZipShort(((this.size != null) ? 8 : 0) + ((this.compressedSize != null) ? 8 : 0) + ((this.relativeHeaderOffset != null) ? 8 : 0) + ((this.diskStart != null) ? 4 : 0));
|
||||
}
|
||||
|
||||
public byte[] getLocalFileDataData() {
|
||||
if (this.size != null || this.compressedSize != null) {
|
||||
if (this.size == null || this.compressedSize == null)
|
||||
throw new IllegalArgumentException("Zip64 extended information must contain both size values in the local file header.");
|
||||
byte[] data = new byte[16];
|
||||
addSizes(data);
|
||||
return data;
|
||||
}
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
public byte[] getCentralDirectoryData() {
|
||||
byte[] data = new byte[getCentralDirectoryLength().getValue()];
|
||||
int off = addSizes(data);
|
||||
if (this.relativeHeaderOffset != null) {
|
||||
System.arraycopy(this.relativeHeaderOffset.getBytes(), 0, data, off, 8);
|
||||
off += 8;
|
||||
}
|
||||
if (this.diskStart != null) {
|
||||
System.arraycopy(this.diskStart.getBytes(), 0, data, off, 4);
|
||||
off += 4;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public void parseFromLocalFileData(byte[] buffer, int offset, int length) throws ZipException {
|
||||
if (length == 0)
|
||||
return;
|
||||
if (length < 16)
|
||||
throw new ZipException("Zip64 extended information must contain both size values in the local file header.");
|
||||
this.size = new ZipEightByteInteger(buffer, offset);
|
||||
offset += 8;
|
||||
this.compressedSize = new ZipEightByteInteger(buffer, offset);
|
||||
offset += 8;
|
||||
int remaining = length - 16;
|
||||
if (remaining >= 8) {
|
||||
this.relativeHeaderOffset = new ZipEightByteInteger(buffer, offset);
|
||||
offset += 8;
|
||||
remaining -= 8;
|
||||
}
|
||||
if (remaining >= 4) {
|
||||
this.diskStart = new ZipLong(buffer, offset);
|
||||
offset += 4;
|
||||
remaining -= 4;
|
||||
}
|
||||
}
|
||||
|
||||
public void parseFromCentralDirectoryData(byte[] buffer, int offset, int length) throws ZipException {
|
||||
this.rawCentralDirectoryData = new byte[length];
|
||||
System.arraycopy(buffer, offset, this.rawCentralDirectoryData, 0, length);
|
||||
if (length >= 28) {
|
||||
parseFromLocalFileData(buffer, offset, length);
|
||||
} else if (length == 24) {
|
||||
this.size = new ZipEightByteInteger(buffer, offset);
|
||||
offset += 8;
|
||||
this.compressedSize = new ZipEightByteInteger(buffer, offset);
|
||||
offset += 8;
|
||||
this.relativeHeaderOffset = new ZipEightByteInteger(buffer, offset);
|
||||
} else if (length % 8 == 4) {
|
||||
this.diskStart = new ZipLong(buffer, offset + length - 4);
|
||||
}
|
||||
}
|
||||
|
||||
public void reparseCentralDirectoryData(boolean hasUncompressedSize, boolean hasCompressedSize, boolean hasRelativeHeaderOffset, boolean hasDiskStart) throws ZipException {
|
||||
if (this.rawCentralDirectoryData != null) {
|
||||
int expectedLength = (hasUncompressedSize ? 8 : 0) + (hasCompressedSize ? 8 : 0) + (hasRelativeHeaderOffset ? 8 : 0) + (hasDiskStart ? 4 : 0);
|
||||
if (this.rawCentralDirectoryData.length < expectedLength)
|
||||
throw new ZipException("central directory zip64 extended information extra field's length doesn't match central directory data. Expected length " + expectedLength + " but is " + this.rawCentralDirectoryData.length);
|
||||
int offset = 0;
|
||||
if (hasUncompressedSize) {
|
||||
this.size = new ZipEightByteInteger(this.rawCentralDirectoryData, offset);
|
||||
offset += 8;
|
||||
}
|
||||
if (hasCompressedSize) {
|
||||
this.compressedSize = new ZipEightByteInteger(this.rawCentralDirectoryData, offset);
|
||||
offset += 8;
|
||||
}
|
||||
if (hasRelativeHeaderOffset) {
|
||||
this.relativeHeaderOffset = new ZipEightByteInteger(this.rawCentralDirectoryData, offset);
|
||||
offset += 8;
|
||||
}
|
||||
if (hasDiskStart) {
|
||||
this.diskStart = new ZipLong(this.rawCentralDirectoryData, offset);
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ZipEightByteInteger getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public void setSize(ZipEightByteInteger size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public ZipEightByteInteger getCompressedSize() {
|
||||
return this.compressedSize;
|
||||
}
|
||||
|
||||
public void setCompressedSize(ZipEightByteInteger compressedSize) {
|
||||
this.compressedSize = compressedSize;
|
||||
}
|
||||
|
||||
public ZipEightByteInteger getRelativeHeaderOffset() {
|
||||
return this.relativeHeaderOffset;
|
||||
}
|
||||
|
||||
public void setRelativeHeaderOffset(ZipEightByteInteger rho) {
|
||||
this.relativeHeaderOffset = rho;
|
||||
}
|
||||
|
||||
public ZipLong getDiskStartNumber() {
|
||||
return this.diskStart;
|
||||
}
|
||||
|
||||
public void setDiskStartNumber(ZipLong ds) {
|
||||
this.diskStart = ds;
|
||||
}
|
||||
|
||||
private int addSizes(byte[] data) {
|
||||
int off = 0;
|
||||
if (this.size != null) {
|
||||
System.arraycopy(this.size.getBytes(), 0, data, 0, 8);
|
||||
off += 8;
|
||||
}
|
||||
if (this.compressedSize != null) {
|
||||
System.arraycopy(this.compressedSize.getBytes(), 0, data, off, 8);
|
||||
off += 8;
|
||||
}
|
||||
return off;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
public enum Zip64Mode {
|
||||
Always, Never, AsNeeded;
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
public class Zip64RequiredException extends ZipException {
|
||||
private static final long serialVersionUID = 20110809L;
|
||||
|
||||
static final String ARCHIVE_TOO_BIG_MESSAGE = "archive's size exceeds the limit of 4GByte.";
|
||||
|
||||
static final String TOO_MANY_ENTRIES_MESSAGE = "archive contains more than 65535 entries.";
|
||||
|
||||
static String getEntryTooBigMessage(ZipArchiveEntry ze) {
|
||||
return ze.getName() + "'s size exceeds the limit of 4GByte.";
|
||||
}
|
||||
|
||||
public Zip64RequiredException(String reason) {
|
||||
super(reason);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,344 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipException;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
|
||||
public class ZipArchiveEntry extends ZipEntry implements ArchiveEntry {
|
||||
public static final int PLATFORM_UNIX = 3;
|
||||
|
||||
public static final int PLATFORM_FAT = 0;
|
||||
|
||||
private static final int SHORT_MASK = 65535;
|
||||
|
||||
private static final int SHORT_SHIFT = 16;
|
||||
|
||||
private static final byte[] EMPTY = new byte[0];
|
||||
|
||||
private int method = -1;
|
||||
|
||||
private long size = -1L;
|
||||
|
||||
private int internalAttributes = 0;
|
||||
|
||||
private int platform = 0;
|
||||
|
||||
private long externalAttributes = 0L;
|
||||
|
||||
private LinkedHashMap<ZipShort, ZipExtraField> extraFields = null;
|
||||
|
||||
private UnparseableExtraFieldData unparseableExtra = null;
|
||||
|
||||
private String name = null;
|
||||
|
||||
private byte[] rawName = null;
|
||||
|
||||
private GeneralPurposeBit gpb = new GeneralPurposeBit();
|
||||
|
||||
public ZipArchiveEntry(String name) {
|
||||
super(name);
|
||||
setName(name);
|
||||
}
|
||||
|
||||
public ZipArchiveEntry(ZipEntry entry) throws ZipException {
|
||||
super(entry);
|
||||
setName(entry.getName());
|
||||
byte[] extra = entry.getExtra();
|
||||
if (extra != null) {
|
||||
setExtraFields(ExtraFieldUtils.parse(extra, true, ExtraFieldUtils.UnparseableExtraField.READ));
|
||||
} else {
|
||||
setExtra();
|
||||
}
|
||||
setMethod(entry.getMethod());
|
||||
this.size = entry.getSize();
|
||||
}
|
||||
|
||||
public ZipArchiveEntry(ZipArchiveEntry entry) throws ZipException {
|
||||
this((ZipEntry)entry);
|
||||
setInternalAttributes(entry.getInternalAttributes());
|
||||
setExternalAttributes(entry.getExternalAttributes());
|
||||
setExtraFields(entry.getExtraFields(true));
|
||||
}
|
||||
|
||||
protected ZipArchiveEntry() {
|
||||
this("");
|
||||
}
|
||||
|
||||
public ZipArchiveEntry(File inputFile, String entryName) {
|
||||
this((inputFile.isDirectory() && !entryName.endsWith("/")) ? (entryName + "/") : entryName);
|
||||
if (inputFile.isFile())
|
||||
setSize(inputFile.length());
|
||||
setTime(inputFile.lastModified());
|
||||
}
|
||||
|
||||
public Object clone() {
|
||||
ZipArchiveEntry e = (ZipArchiveEntry)super.clone();
|
||||
e.setInternalAttributes(getInternalAttributes());
|
||||
e.setExternalAttributes(getExternalAttributes());
|
||||
e.setExtraFields(getExtraFields(true));
|
||||
return e;
|
||||
}
|
||||
|
||||
public int getMethod() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
public void setMethod(int method) {
|
||||
if (method < 0)
|
||||
throw new IllegalArgumentException("ZIP compression method can not be negative: " + method);
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public int getInternalAttributes() {
|
||||
return this.internalAttributes;
|
||||
}
|
||||
|
||||
public void setInternalAttributes(int value) {
|
||||
this.internalAttributes = value;
|
||||
}
|
||||
|
||||
public long getExternalAttributes() {
|
||||
return this.externalAttributes;
|
||||
}
|
||||
|
||||
public void setExternalAttributes(long value) {
|
||||
this.externalAttributes = value;
|
||||
}
|
||||
|
||||
public void setUnixMode(int mode) {
|
||||
setExternalAttributes((long)(mode << 16 | (((mode & 0x80) == 0) ? 1 : 0) | (isDirectory() ? 16 : 0)));
|
||||
this.platform = 3;
|
||||
}
|
||||
|
||||
public int getUnixMode() {
|
||||
return (this.platform != 3) ? 0 : (int)(getExternalAttributes() >> 16L & 0xFFFFL);
|
||||
}
|
||||
|
||||
public boolean isUnixSymlink() {
|
||||
return ((getUnixMode() & 0xA000) == 40960);
|
||||
}
|
||||
|
||||
public int getPlatform() {
|
||||
return this.platform;
|
||||
}
|
||||
|
||||
protected void setPlatform(int platform) {
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
public void setExtraFields(ZipExtraField[] fields) {
|
||||
this.extraFields = new LinkedHashMap<ZipShort, ZipExtraField>();
|
||||
for (ZipExtraField field : fields) {
|
||||
if (field instanceof UnparseableExtraFieldData) {
|
||||
this.unparseableExtra = (UnparseableExtraFieldData)field;
|
||||
} else {
|
||||
this.extraFields.put(field.getHeaderId(), field);
|
||||
}
|
||||
}
|
||||
setExtra();
|
||||
}
|
||||
|
||||
public ZipExtraField[] getExtraFields() {
|
||||
return getExtraFields(false);
|
||||
}
|
||||
|
||||
public ZipExtraField[] getExtraFields(boolean includeUnparseable) {
|
||||
if (this.extraFields == null)
|
||||
return (!includeUnparseable || this.unparseableExtra == null) ? new ZipExtraField[0] : new ZipExtraField[] { this.unparseableExtra };
|
||||
List<ZipExtraField> result = new ArrayList<ZipExtraField>(this.extraFields.values());
|
||||
if (includeUnparseable && this.unparseableExtra != null)
|
||||
result.add(this.unparseableExtra);
|
||||
return result.<ZipExtraField>toArray(new ZipExtraField[0]);
|
||||
}
|
||||
|
||||
public void addExtraField(ZipExtraField ze) {
|
||||
if (ze instanceof UnparseableExtraFieldData) {
|
||||
this.unparseableExtra = (UnparseableExtraFieldData)ze;
|
||||
} else {
|
||||
if (this.extraFields == null)
|
||||
this.extraFields = new LinkedHashMap<ZipShort, ZipExtraField>();
|
||||
this.extraFields.put(ze.getHeaderId(), ze);
|
||||
}
|
||||
setExtra();
|
||||
}
|
||||
|
||||
public void addAsFirstExtraField(ZipExtraField ze) {
|
||||
if (ze instanceof UnparseableExtraFieldData) {
|
||||
this.unparseableExtra = (UnparseableExtraFieldData)ze;
|
||||
} else {
|
||||
LinkedHashMap<ZipShort, ZipExtraField> copy = this.extraFields;
|
||||
this.extraFields = new LinkedHashMap<ZipShort, ZipExtraField>();
|
||||
this.extraFields.put(ze.getHeaderId(), ze);
|
||||
if (copy != null) {
|
||||
copy.remove(ze.getHeaderId());
|
||||
this.extraFields.putAll(copy);
|
||||
}
|
||||
}
|
||||
setExtra();
|
||||
}
|
||||
|
||||
public void removeExtraField(ZipShort type) {
|
||||
if (this.extraFields == null)
|
||||
throw new NoSuchElementException();
|
||||
if (this.extraFields.remove(type) == null)
|
||||
throw new NoSuchElementException();
|
||||
setExtra();
|
||||
}
|
||||
|
||||
public void removeUnparseableExtraFieldData() {
|
||||
if (this.unparseableExtra == null)
|
||||
throw new NoSuchElementException();
|
||||
this.unparseableExtra = null;
|
||||
setExtra();
|
||||
}
|
||||
|
||||
public ZipExtraField getExtraField(ZipShort type) {
|
||||
if (this.extraFields != null)
|
||||
return this.extraFields.get(type);
|
||||
return null;
|
||||
}
|
||||
|
||||
public UnparseableExtraFieldData getUnparseableExtraFieldData() {
|
||||
return this.unparseableExtra;
|
||||
}
|
||||
|
||||
public void setExtra(byte[] extra) throws RuntimeException {
|
||||
try {
|
||||
ZipExtraField[] local = ExtraFieldUtils.parse(extra, true, ExtraFieldUtils.UnparseableExtraField.READ);
|
||||
mergeExtraFields(local, true);
|
||||
} catch (ZipException e) {
|
||||
throw new RuntimeException("Error parsing extra fields for entry: " + getName() + " - " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setExtra() {
|
||||
super.setExtra(ExtraFieldUtils.mergeLocalFileDataData(getExtraFields(true)));
|
||||
}
|
||||
|
||||
public void setCentralDirectoryExtra(byte[] b) {
|
||||
try {
|
||||
ZipExtraField[] central = ExtraFieldUtils.parse(b, false, ExtraFieldUtils.UnparseableExtraField.READ);
|
||||
mergeExtraFields(central, false);
|
||||
} catch (ZipException e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getLocalFileDataExtra() {
|
||||
byte[] extra = getExtra();
|
||||
return (extra != null) ? extra : EMPTY;
|
||||
}
|
||||
|
||||
public byte[] getCentralDirectoryExtra() {
|
||||
return ExtraFieldUtils.mergeCentralDirectoryData(getExtraFields(true));
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return (this.name == null) ? super.getName() : this.name;
|
||||
}
|
||||
|
||||
public boolean isDirectory() {
|
||||
return getName().endsWith("/");
|
||||
}
|
||||
|
||||
protected void setName(String name) {
|
||||
if (name != null && getPlatform() == 0 && name.indexOf("/") == -1)
|
||||
name = name.replace('\\', '/');
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public void setSize(long size) {
|
||||
if (size < 0L)
|
||||
throw new IllegalArgumentException("invalid entry size");
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
protected void setName(String name, byte[] rawName) {
|
||||
setName(name);
|
||||
this.rawName = rawName;
|
||||
}
|
||||
|
||||
public byte[] getRawName() {
|
||||
if (this.rawName != null) {
|
||||
byte[] b = new byte[this.rawName.length];
|
||||
System.arraycopy(this.rawName, 0, b, 0, this.rawName.length);
|
||||
return b;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return getName().hashCode();
|
||||
}
|
||||
|
||||
public GeneralPurposeBit getGeneralPurposeBit() {
|
||||
return this.gpb;
|
||||
}
|
||||
|
||||
public void setGeneralPurposeBit(GeneralPurposeBit b) {
|
||||
this.gpb = b;
|
||||
}
|
||||
|
||||
private void mergeExtraFields(ZipExtraField[] f, boolean local) throws ZipException {
|
||||
if (this.extraFields == null) {
|
||||
setExtraFields(f);
|
||||
} else {
|
||||
for (ZipExtraField element : f) {
|
||||
ZipExtraField existing;
|
||||
if (element instanceof UnparseableExtraFieldData) {
|
||||
existing = this.unparseableExtra;
|
||||
} else {
|
||||
existing = getExtraField(element.getHeaderId());
|
||||
}
|
||||
if (existing == null) {
|
||||
addExtraField(element);
|
||||
} else if (local) {
|
||||
byte[] b = element.getLocalFileDataData();
|
||||
existing.parseFromLocalFileData(b, 0, b.length);
|
||||
} else {
|
||||
byte[] b = element.getCentralDirectoryData();
|
||||
existing.parseFromCentralDirectoryData(b, 0, b.length);
|
||||
}
|
||||
}
|
||||
setExtra();
|
||||
}
|
||||
}
|
||||
|
||||
public Date getLastModifiedDate() {
|
||||
return new Date(getTime());
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
ZipArchiveEntry other = (ZipArchiveEntry)obj;
|
||||
String myName = getName();
|
||||
String otherName = other.getName();
|
||||
if (myName == null) {
|
||||
if (otherName != null)
|
||||
return false;
|
||||
} else if (!myName.equals(otherName)) {
|
||||
return false;
|
||||
}
|
||||
String myComment = getComment();
|
||||
String otherComment = other.getComment();
|
||||
if (myComment == null)
|
||||
myComment = "";
|
||||
if (otherComment == null)
|
||||
otherComment = "";
|
||||
return (getTime() == other.getTime() && myComment.equals(otherComment) && getInternalAttributes() == other.getInternalAttributes() && getPlatform() == other.getPlatform() && getExternalAttributes() == other.getExternalAttributes() && getMethod() == other.getMethod() && getSize() == other.getSize() && getCrc() == other.getCrc() && getCompressedSize() == other.getCompressedSize() && Arrays.equals(getCentralDirectoryExtra(), other.getCentralDirectoryExtra()) && Arrays.equals(getLocalFileDataExtra(), other.getLocalFileDataExtra()) && this.gpb.equals(other.gpb));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,595 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PushbackInputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.zip.CRC32;
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Inflater;
|
||||
import java.util.zip.ZipException;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.ArchiveInputStream;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
|
||||
public class ZipArchiveInputStream extends ArchiveInputStream {
|
||||
private final ZipEncoding zipEncoding;
|
||||
|
||||
private final boolean useUnicodeExtraFields;
|
||||
|
||||
private final InputStream in;
|
||||
|
||||
private final Inflater inf = new Inflater(true);
|
||||
|
||||
private final ByteBuffer buf = ByteBuffer.allocate(512);
|
||||
|
||||
private CurrentEntry current = null;
|
||||
|
||||
private boolean closed = false;
|
||||
|
||||
private boolean hitCentralDirectory = false;
|
||||
|
||||
private ByteArrayInputStream lastStoredEntry = null;
|
||||
|
||||
private boolean allowStoredEntriesWithDataDescriptor = false;
|
||||
|
||||
private static final int LFH_LEN = 30;
|
||||
|
||||
private static final int CFH_LEN = 46;
|
||||
|
||||
private static final long TWO_EXP_32 = 4294967296L;
|
||||
|
||||
private final byte[] LFH_BUF = new byte[30];
|
||||
|
||||
private final byte[] SKIP_BUF = new byte[1024];
|
||||
|
||||
private final byte[] SHORT_BUF = new byte[2];
|
||||
|
||||
private final byte[] WORD_BUF = new byte[4];
|
||||
|
||||
private final byte[] TWO_DWORD_BUF = new byte[16];
|
||||
|
||||
private int entriesRead = 0;
|
||||
|
||||
public ZipArchiveInputStream(InputStream inputStream) {
|
||||
this(inputStream, "UTF8");
|
||||
}
|
||||
|
||||
public ZipArchiveInputStream(InputStream inputStream, String encoding) {
|
||||
this(inputStream, encoding, true);
|
||||
}
|
||||
|
||||
public ZipArchiveInputStream(InputStream inputStream, String encoding, boolean useUnicodeExtraFields) {
|
||||
this(inputStream, encoding, useUnicodeExtraFields, false);
|
||||
}
|
||||
|
||||
public ZipArchiveInputStream(InputStream inputStream, String encoding, boolean useUnicodeExtraFields, boolean allowStoredEntriesWithDataDescriptor) {
|
||||
this.zipEncoding = ZipEncodingHelper.getZipEncoding(encoding);
|
||||
this.useUnicodeExtraFields = useUnicodeExtraFields;
|
||||
this.in = new PushbackInputStream(inputStream, this.buf.capacity());
|
||||
this.allowStoredEntriesWithDataDescriptor = allowStoredEntriesWithDataDescriptor;
|
||||
this.buf.limit(0);
|
||||
}
|
||||
|
||||
public ZipArchiveEntry getNextZipEntry() throws IOException {
|
||||
boolean firstEntry = true;
|
||||
if (this.closed || this.hitCentralDirectory)
|
||||
return null;
|
||||
if (this.current != null) {
|
||||
closeEntry();
|
||||
firstEntry = false;
|
||||
}
|
||||
try {
|
||||
if (firstEntry) {
|
||||
readFirstLocalFileHeader(this.LFH_BUF);
|
||||
} else {
|
||||
readFully(this.LFH_BUF);
|
||||
}
|
||||
} catch (EOFException e) {
|
||||
return null;
|
||||
}
|
||||
ZipLong sig = new ZipLong(this.LFH_BUF);
|
||||
if (sig.equals(ZipLong.CFH_SIG) || sig.equals(ZipLong.AED_SIG)) {
|
||||
this.hitCentralDirectory = true;
|
||||
skipRemainderOfArchive();
|
||||
}
|
||||
if (!sig.equals(ZipLong.LFH_SIG))
|
||||
return null;
|
||||
int off = 4;
|
||||
this.current = new CurrentEntry();
|
||||
int versionMadeBy = ZipShort.getValue(this.LFH_BUF, off);
|
||||
off += 2;
|
||||
this.current.entry.setPlatform(versionMadeBy >> 8 & 0xF);
|
||||
GeneralPurposeBit gpFlag = GeneralPurposeBit.parse(this.LFH_BUF, off);
|
||||
boolean hasUTF8Flag = gpFlag.usesUTF8ForNames();
|
||||
ZipEncoding entryEncoding = hasUTF8Flag ? ZipEncodingHelper.UTF8_ZIP_ENCODING : this.zipEncoding;
|
||||
this.current.hasDataDescriptor = gpFlag.usesDataDescriptor();
|
||||
this.current.entry.setGeneralPurposeBit(gpFlag);
|
||||
off += 2;
|
||||
this.current.entry.setMethod(ZipShort.getValue(this.LFH_BUF, off));
|
||||
off += 2;
|
||||
long time = ZipUtil.dosToJavaTime(ZipLong.getValue(this.LFH_BUF, off));
|
||||
this.current.entry.setTime(time);
|
||||
off += 4;
|
||||
ZipLong size = null, cSize = null;
|
||||
if (!this.current.hasDataDescriptor) {
|
||||
this.current.entry.setCrc(ZipLong.getValue(this.LFH_BUF, off));
|
||||
off += 4;
|
||||
cSize = new ZipLong(this.LFH_BUF, off);
|
||||
off += 4;
|
||||
size = new ZipLong(this.LFH_BUF, off);
|
||||
off += 4;
|
||||
} else {
|
||||
off += 12;
|
||||
}
|
||||
int fileNameLen = ZipShort.getValue(this.LFH_BUF, off);
|
||||
off += 2;
|
||||
int extraLen = ZipShort.getValue(this.LFH_BUF, off);
|
||||
off += 2;
|
||||
byte[] fileName = new byte[fileNameLen];
|
||||
readFully(fileName);
|
||||
this.current.entry.setName(entryEncoding.decode(fileName), fileName);
|
||||
byte[] extraData = new byte[extraLen];
|
||||
readFully(extraData);
|
||||
this.current.entry.setExtra(extraData);
|
||||
if (!hasUTF8Flag && this.useUnicodeExtraFields)
|
||||
ZipUtil.setNameAndCommentFromExtraFields(this.current.entry, fileName, null);
|
||||
processZip64Extra(size, cSize);
|
||||
if (this.current.entry.getCompressedSize() != -1L)
|
||||
if (this.current.entry.getMethod() == ZipMethod.UNSHRINKING.getCode()) {
|
||||
this.current.in = new UnshrinkingInputStream(new BoundedInputStream(this.in, this.current.entry.getCompressedSize()));
|
||||
} else if (this.current.entry.getMethod() == ZipMethod.IMPLODING.getCode()) {
|
||||
this.current.in = new ExplodingInputStream(this.current.entry.getGeneralPurposeBit().getSlidingDictionarySize(), this.current.entry.getGeneralPurposeBit().getNumberOfShannonFanoTrees(), new BoundedInputStream(this.in, this.current.entry.getCompressedSize()));
|
||||
}
|
||||
this.entriesRead++;
|
||||
return this.current.entry;
|
||||
}
|
||||
|
||||
private void readFirstLocalFileHeader(byte[] lfh) throws IOException {
|
||||
readFully(lfh);
|
||||
ZipLong sig = new ZipLong(lfh);
|
||||
if (sig.equals(ZipLong.DD_SIG))
|
||||
throw new UnsupportedZipFeatureException(UnsupportedZipFeatureException.Feature.SPLITTING);
|
||||
if (sig.equals(ZipLong.SINGLE_SEGMENT_SPLIT_MARKER)) {
|
||||
byte[] missedLfhBytes = new byte[4];
|
||||
readFully(missedLfhBytes);
|
||||
System.arraycopy(lfh, 4, lfh, 0, 26);
|
||||
System.arraycopy(missedLfhBytes, 0, lfh, 26, 4);
|
||||
}
|
||||
}
|
||||
|
||||
private void processZip64Extra(ZipLong size, ZipLong cSize) {
|
||||
Zip64ExtendedInformationExtraField z64 = (Zip64ExtendedInformationExtraField)this.current.entry.getExtraField(Zip64ExtendedInformationExtraField.HEADER_ID);
|
||||
this.current.usesZip64 = (z64 != null);
|
||||
if (!this.current.hasDataDescriptor)
|
||||
if (z64 != null && (cSize.equals(ZipLong.ZIP64_MAGIC) || size.equals(ZipLong.ZIP64_MAGIC))) {
|
||||
this.current.entry.setCompressedSize(z64.getCompressedSize().getLongValue());
|
||||
this.current.entry.setSize(z64.getSize().getLongValue());
|
||||
} else {
|
||||
this.current.entry.setCompressedSize(cSize.getValue());
|
||||
this.current.entry.setSize(size.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public ArchiveEntry getNextEntry() throws IOException {
|
||||
return getNextZipEntry();
|
||||
}
|
||||
|
||||
public boolean canReadEntryData(ArchiveEntry ae) {
|
||||
if (ae instanceof ZipArchiveEntry) {
|
||||
ZipArchiveEntry ze = (ZipArchiveEntry)ae;
|
||||
return (ZipUtil.canHandleEntryData(ze) && supportsDataDescriptorFor(ze));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int read(byte[] buffer, int offset, int length) throws IOException {
|
||||
int read;
|
||||
if (this.closed)
|
||||
throw new IOException("The stream is closed");
|
||||
if (this.current == null)
|
||||
return -1;
|
||||
if (offset > buffer.length || length < 0 || offset < 0 || buffer.length - offset < length)
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
ZipUtil.checkRequestedFeatures(this.current.entry);
|
||||
if (!supportsDataDescriptorFor(this.current.entry))
|
||||
throw new UnsupportedZipFeatureException(UnsupportedZipFeatureException.Feature.DATA_DESCRIPTOR, this.current.entry);
|
||||
if (this.current.entry.getMethod() == 0) {
|
||||
read = readStored(buffer, offset, length);
|
||||
} else if (this.current.entry.getMethod() == 8) {
|
||||
read = readDeflated(buffer, offset, length);
|
||||
} else if (this.current.entry.getMethod() == ZipMethod.UNSHRINKING.getCode() || this.current.entry.getMethod() == ZipMethod.IMPLODING.getCode()) {
|
||||
read = this.current.in.read(buffer, offset, length);
|
||||
} else {
|
||||
throw new UnsupportedZipFeatureException(ZipMethod.getMethodByCode(this.current.entry.getMethod()), this.current.entry);
|
||||
}
|
||||
if (read >= 0)
|
||||
this.current.crc.update(buffer, offset, read);
|
||||
return read;
|
||||
}
|
||||
|
||||
private int readStored(byte[] buffer, int offset, int length) throws IOException {
|
||||
if (this.current.hasDataDescriptor) {
|
||||
if (this.lastStoredEntry == null)
|
||||
readStoredEntry();
|
||||
return this.lastStoredEntry.read(buffer, offset, length);
|
||||
}
|
||||
long csize = this.current.entry.getSize();
|
||||
if (this.current.bytesRead >= csize)
|
||||
return -1;
|
||||
if (this.buf.position() >= this.buf.limit()) {
|
||||
this.buf.position(0);
|
||||
int l = this.in.read(this.buf.array());
|
||||
if (l == -1)
|
||||
return -1;
|
||||
this.buf.limit(l);
|
||||
count(l);
|
||||
this.current.bytesReadFromStream += (long)l;
|
||||
}
|
||||
int toRead = Math.min(this.buf.remaining(), length);
|
||||
if (csize - this.current.bytesRead < (long)toRead)
|
||||
toRead = (int)(csize - this.current.bytesRead);
|
||||
this.buf.get(buffer, offset, toRead);
|
||||
this.current.bytesRead += (long)toRead;
|
||||
return toRead;
|
||||
}
|
||||
|
||||
private int readDeflated(byte[] buffer, int offset, int length) throws IOException {
|
||||
int read = readFromInflater(buffer, offset, length);
|
||||
if (read <= 0) {
|
||||
if (this.inf.finished())
|
||||
return -1;
|
||||
if (this.inf.needsDictionary())
|
||||
throw new ZipException("This archive needs a preset dictionary which is not supported by Commons Compress.");
|
||||
if (read == -1)
|
||||
throw new IOException("Truncated ZIP file");
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
private int readFromInflater(byte[] buffer, int offset, int length) throws IOException {
|
||||
int read = 0;
|
||||
do {
|
||||
if (this.inf.needsInput()) {
|
||||
int l = fill();
|
||||
if (l > 0) {
|
||||
this.current.bytesReadFromStream += (long)this.buf.limit();
|
||||
} else {
|
||||
if (l == -1)
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
read = this.inf.inflate(buffer, offset, length);
|
||||
} catch (DataFormatException e) {
|
||||
throw (IOException)new ZipException(e.getMessage()).initCause(e);
|
||||
}
|
||||
} while (read == 0 && this.inf.needsInput());
|
||||
return read;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (!this.closed) {
|
||||
this.closed = true;
|
||||
this.in.close();
|
||||
this.inf.end();
|
||||
}
|
||||
}
|
||||
|
||||
public long skip(long value) throws IOException {
|
||||
if (value >= 0L) {
|
||||
long skipped = 0L;
|
||||
while (skipped < value) {
|
||||
long rem = value - skipped;
|
||||
int x = read(this.SKIP_BUF, 0, (int)(((long)this.SKIP_BUF.length > rem) ? rem : (long)this.SKIP_BUF.length));
|
||||
if (x == -1)
|
||||
return skipped;
|
||||
skipped += (long)x;
|
||||
}
|
||||
return skipped;
|
||||
}
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
public static boolean matches(byte[] signature, int length) {
|
||||
if (length < ZipArchiveOutputStream.LFH_SIG.length)
|
||||
return false;
|
||||
return (checksig(signature, ZipArchiveOutputStream.LFH_SIG) || checksig(signature, ZipArchiveOutputStream.EOCD_SIG) || checksig(signature, ZipArchiveOutputStream.DD_SIG) || checksig(signature, ZipLong.SINGLE_SEGMENT_SPLIT_MARKER.getBytes()));
|
||||
}
|
||||
|
||||
private static boolean checksig(byte[] signature, byte[] expected) {
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
if (signature[i] != expected[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void closeEntry() throws IOException {
|
||||
if (this.closed)
|
||||
throw new IOException("The stream is closed");
|
||||
if (this.current == null)
|
||||
return;
|
||||
if (this.current.bytesReadFromStream <= this.current.entry.getCompressedSize() && !this.current.hasDataDescriptor) {
|
||||
drainCurrentEntryData();
|
||||
} else {
|
||||
skip(Long.MAX_VALUE);
|
||||
long inB = (this.current.entry.getMethod() == 8) ? getBytesInflated() : this.current.bytesRead;
|
||||
int diff = (int)(this.current.bytesReadFromStream - inB);
|
||||
if (diff > 0)
|
||||
pushback(this.buf.array(), this.buf.limit() - diff, diff);
|
||||
}
|
||||
if (this.lastStoredEntry == null && this.current.hasDataDescriptor)
|
||||
readDataDescriptor();
|
||||
this.inf.reset();
|
||||
this.buf.clear().flip();
|
||||
this.current = null;
|
||||
this.lastStoredEntry = null;
|
||||
}
|
||||
|
||||
private void drainCurrentEntryData() throws IOException {
|
||||
long remaining = this.current.entry.getCompressedSize() - this.current.bytesReadFromStream;
|
||||
while (remaining > 0L) {
|
||||
long n = (long)this.in.read(this.buf.array(), 0, (int)Math.min((long)this.buf.capacity(), remaining));
|
||||
if (n < 0L)
|
||||
throw new EOFException("Truncated ZIP entry: " + this.current.entry.getName());
|
||||
count(n);
|
||||
remaining -= n;
|
||||
}
|
||||
}
|
||||
|
||||
private long getBytesInflated() {
|
||||
long inB = this.inf.getBytesRead();
|
||||
if (this.current.bytesReadFromStream >= 4294967296L)
|
||||
while (inB + 4294967296L <= this.current.bytesReadFromStream)
|
||||
inB += 4294967296L;
|
||||
return inB;
|
||||
}
|
||||
|
||||
private int fill() throws IOException {
|
||||
if (this.closed)
|
||||
throw new IOException("The stream is closed");
|
||||
int length = this.in.read(this.buf.array());
|
||||
if (length > 0) {
|
||||
this.buf.limit(length);
|
||||
count(this.buf.limit());
|
||||
this.inf.setInput(this.buf.array(), 0, this.buf.limit());
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
private void readFully(byte[] b) throws IOException {
|
||||
int count = IOUtils.readFully(this.in, b);
|
||||
count(count);
|
||||
if (count < b.length)
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
private void readDataDescriptor() throws IOException {
|
||||
readFully(this.WORD_BUF);
|
||||
ZipLong val = new ZipLong(this.WORD_BUF);
|
||||
if (ZipLong.DD_SIG.equals(val)) {
|
||||
readFully(this.WORD_BUF);
|
||||
val = new ZipLong(this.WORD_BUF);
|
||||
}
|
||||
this.current.entry.setCrc(val.getValue());
|
||||
readFully(this.TWO_DWORD_BUF);
|
||||
ZipLong potentialSig = new ZipLong(this.TWO_DWORD_BUF, 8);
|
||||
if (potentialSig.equals(ZipLong.CFH_SIG) || potentialSig.equals(ZipLong.LFH_SIG)) {
|
||||
pushback(this.TWO_DWORD_BUF, 8, 8);
|
||||
this.current.entry.setCompressedSize(ZipLong.getValue(this.TWO_DWORD_BUF));
|
||||
this.current.entry.setSize(ZipLong.getValue(this.TWO_DWORD_BUF, 4));
|
||||
} else {
|
||||
this.current.entry.setCompressedSize(ZipEightByteInteger.getLongValue(this.TWO_DWORD_BUF));
|
||||
this.current.entry.setSize(ZipEightByteInteger.getLongValue(this.TWO_DWORD_BUF, 8));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean supportsDataDescriptorFor(ZipArchiveEntry entry) {
|
||||
return (!entry.getGeneralPurposeBit().usesDataDescriptor() || (this.allowStoredEntriesWithDataDescriptor && entry.getMethod() == 0) || entry.getMethod() == 8);
|
||||
}
|
||||
|
||||
private void readStoredEntry() throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
int off = 0;
|
||||
boolean done = false;
|
||||
int ddLen = this.current.usesZip64 ? 20 : 12;
|
||||
while (!done) {
|
||||
int r = this.in.read(this.buf.array(), off, 512 - off);
|
||||
if (r <= 0)
|
||||
throw new IOException("Truncated ZIP file");
|
||||
if (r + off < 4) {
|
||||
off += r;
|
||||
continue;
|
||||
}
|
||||
done = bufferContainsSignature(bos, off, r, ddLen);
|
||||
if (!done)
|
||||
off = cacheBytesRead(bos, off, r, ddLen);
|
||||
}
|
||||
byte[] b = bos.toByteArray();
|
||||
this.lastStoredEntry = new ByteArrayInputStream(b);
|
||||
}
|
||||
|
||||
private static final byte[] LFH = ZipLong.LFH_SIG.getBytes();
|
||||
|
||||
private static final byte[] CFH = ZipLong.CFH_SIG.getBytes();
|
||||
|
||||
private static final byte[] DD = ZipLong.DD_SIG.getBytes();
|
||||
|
||||
private boolean bufferContainsSignature(ByteArrayOutputStream bos, int offset, int lastRead, int expectedDDLen) throws IOException {
|
||||
boolean done = false;
|
||||
int readTooMuch = 0;
|
||||
for (int i = 0; !done && i < lastRead - 4; i++) {
|
||||
if (this.buf.array()[i] == LFH[0] && this.buf.array()[i + 1] == LFH[1]) {
|
||||
if ((this.buf.array()[i + 2] == LFH[2] && this.buf.array()[i + 3] == LFH[3]) || (this.buf.array()[i] == CFH[2] && this.buf.array()[i + 3] == CFH[3])) {
|
||||
readTooMuch = offset + lastRead - i - expectedDDLen;
|
||||
done = true;
|
||||
} else if (this.buf.array()[i + 2] == DD[2] && this.buf.array()[i + 3] == DD[3]) {
|
||||
readTooMuch = offset + lastRead - i;
|
||||
done = true;
|
||||
}
|
||||
if (done) {
|
||||
pushback(this.buf.array(), offset + lastRead - readTooMuch, readTooMuch);
|
||||
bos.write(this.buf.array(), 0, i);
|
||||
readDataDescriptor();
|
||||
}
|
||||
}
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
private int cacheBytesRead(ByteArrayOutputStream bos, int offset, int lastRead, int expecteDDLen) {
|
||||
int cacheable = offset + lastRead - expecteDDLen - 3;
|
||||
if (cacheable > 0) {
|
||||
bos.write(this.buf.array(), 0, cacheable);
|
||||
System.arraycopy(this.buf.array(), cacheable, this.buf.array(), 0, expecteDDLen + 3);
|
||||
offset = expecteDDLen + 3;
|
||||
} else {
|
||||
offset += lastRead;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
private void pushback(byte[] buf, int offset, int length) throws IOException {
|
||||
((PushbackInputStream)this.in).unread(buf, offset, length);
|
||||
pushedBackBytes((long)length);
|
||||
}
|
||||
|
||||
private void skipRemainderOfArchive() throws IOException {
|
||||
realSkip((long)(this.entriesRead * 46 - 30));
|
||||
findEocdRecord();
|
||||
realSkip(16L);
|
||||
readFully(this.SHORT_BUF);
|
||||
realSkip((long)ZipShort.getValue(this.SHORT_BUF));
|
||||
}
|
||||
|
||||
private void findEocdRecord() throws IOException {
|
||||
int currentByte = -1;
|
||||
boolean skipReadCall = false;
|
||||
while (skipReadCall || (currentByte = readOneByte()) > -1) {
|
||||
skipReadCall = false;
|
||||
if (!isFirstByteOfEocdSig(currentByte))
|
||||
continue;
|
||||
currentByte = readOneByte();
|
||||
if (currentByte != ZipArchiveOutputStream.EOCD_SIG[1]) {
|
||||
if (currentByte == -1)
|
||||
break;
|
||||
skipReadCall = isFirstByteOfEocdSig(currentByte);
|
||||
continue;
|
||||
}
|
||||
currentByte = readOneByte();
|
||||
if (currentByte != ZipArchiveOutputStream.EOCD_SIG[2]) {
|
||||
if (currentByte == -1)
|
||||
break;
|
||||
skipReadCall = isFirstByteOfEocdSig(currentByte);
|
||||
continue;
|
||||
}
|
||||
currentByte = readOneByte();
|
||||
if (currentByte == -1 || currentByte == ZipArchiveOutputStream.EOCD_SIG[3])
|
||||
break;
|
||||
skipReadCall = isFirstByteOfEocdSig(currentByte);
|
||||
}
|
||||
}
|
||||
|
||||
private void realSkip(long value) throws IOException {
|
||||
if (value >= 0L) {
|
||||
long skipped = 0L;
|
||||
while (skipped < value) {
|
||||
long rem = value - skipped;
|
||||
int x = this.in.read(this.SKIP_BUF, 0, (int)(((long)this.SKIP_BUF.length > rem) ? rem : (long)this.SKIP_BUF.length));
|
||||
if (x == -1)
|
||||
return;
|
||||
count(x);
|
||||
skipped += (long)x;
|
||||
}
|
||||
return;
|
||||
}
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
private int readOneByte() throws IOException {
|
||||
int b = this.in.read();
|
||||
if (b != -1)
|
||||
count(1);
|
||||
return b;
|
||||
}
|
||||
|
||||
private boolean isFirstByteOfEocdSig(int b) {
|
||||
return (b == ZipArchiveOutputStream.EOCD_SIG[0]);
|
||||
}
|
||||
|
||||
private static final class CurrentEntry {
|
||||
private CurrentEntry() {}
|
||||
|
||||
private final ZipArchiveEntry entry = new ZipArchiveEntry();
|
||||
|
||||
private boolean hasDataDescriptor;
|
||||
|
||||
private boolean usesZip64;
|
||||
|
||||
private long bytesRead;
|
||||
|
||||
private long bytesReadFromStream;
|
||||
|
||||
private final CRC32 crc = new CRC32();
|
||||
|
||||
private InputStream in;
|
||||
}
|
||||
|
||||
private class BoundedInputStream extends InputStream {
|
||||
private final InputStream in;
|
||||
|
||||
private final long max;
|
||||
|
||||
private long pos = 0L;
|
||||
|
||||
public BoundedInputStream(InputStream in, long size) {
|
||||
this.max = size;
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
if (this.max >= 0L && this.pos >= this.max)
|
||||
return -1;
|
||||
int result = this.in.read();
|
||||
this.pos++;
|
||||
ZipArchiveInputStream.this.count(1);
|
||||
ZipArchiveInputStream.this.current.bytesReadFromStream++;
|
||||
return result;
|
||||
}
|
||||
|
||||
public int read(byte[] b) throws IOException {
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
if (this.max >= 0L && this.pos >= this.max)
|
||||
return -1;
|
||||
long maxRead = (this.max >= 0L) ? Math.min((long)len, this.max - this.pos) : (long)len;
|
||||
int bytesRead = this.in.read(b, off, (int)maxRead);
|
||||
if (bytesRead == -1)
|
||||
return -1;
|
||||
this.pos += (long)bytesRead;
|
||||
ZipArchiveInputStream.this.count(bytesRead);
|
||||
ZipArchiveInputStream.this.current.bytesReadFromStream += (long)bytesRead;
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
public long skip(long n) throws IOException {
|
||||
long toSkip = (this.max >= 0L) ? Math.min(n, this.max - this.pos) : n;
|
||||
long skippedBytes = this.in.skip(toSkip);
|
||||
this.pos += skippedBytes;
|
||||
return skippedBytes;
|
||||
}
|
||||
|
||||
public int available() throws IOException {
|
||||
if (this.max >= 0L && this.pos >= this.max)
|
||||
return 0;
|
||||
return this.in.available();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,673 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.CRC32;
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.ZipException;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.ArchiveOutputStream;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
|
||||
public class ZipArchiveOutputStream extends ArchiveOutputStream {
|
||||
static final int BUFFER_SIZE = 512;
|
||||
|
||||
protected boolean finished = false;
|
||||
|
||||
private static final int DEFLATER_BLOCK_SIZE = 8192;
|
||||
|
||||
public static final int DEFLATED = 8;
|
||||
|
||||
public static final int DEFAULT_COMPRESSION = -1;
|
||||
|
||||
public static final int STORED = 0;
|
||||
|
||||
static final String DEFAULT_ENCODING = "UTF8";
|
||||
|
||||
@Deprecated
|
||||
public static final int EFS_FLAG = 2048;
|
||||
|
||||
private static final byte[] EMPTY = new byte[0];
|
||||
|
||||
private CurrentEntry entry;
|
||||
|
||||
private String comment = "";
|
||||
|
||||
private int level = -1;
|
||||
|
||||
private boolean hasCompressionLevelChanged = false;
|
||||
|
||||
private int method = 8;
|
||||
|
||||
private final List<ZipArchiveEntry> entries = new LinkedList<ZipArchiveEntry>();
|
||||
|
||||
private final CRC32 crc = new CRC32();
|
||||
|
||||
private long written = 0L;
|
||||
|
||||
private long cdOffset = 0L;
|
||||
|
||||
private long cdLength = 0L;
|
||||
|
||||
private static final byte[] ZERO = new byte[] { 0, 0 };
|
||||
|
||||
private static final byte[] LZERO = new byte[] { 0, 0, 0, 0 };
|
||||
|
||||
private final Map<ZipArchiveEntry, Long> offsets = new HashMap<ZipArchiveEntry, Long>();
|
||||
|
||||
private String encoding = "UTF8";
|
||||
|
||||
private ZipEncoding zipEncoding = ZipEncodingHelper.getZipEncoding("UTF8");
|
||||
|
||||
protected final Deflater def = new Deflater(this.level, true);
|
||||
|
||||
private final byte[] buf = new byte[512];
|
||||
|
||||
private final RandomAccessFile raf;
|
||||
|
||||
private final OutputStream out;
|
||||
|
||||
private boolean useUTF8Flag = true;
|
||||
|
||||
private boolean fallbackToUTF8 = false;
|
||||
|
||||
private UnicodeExtraFieldPolicy createUnicodeExtraFields = UnicodeExtraFieldPolicy.NEVER;
|
||||
|
||||
private boolean hasUsedZip64 = false;
|
||||
|
||||
private Zip64Mode zip64Mode = Zip64Mode.AsNeeded;
|
||||
|
||||
public ZipArchiveOutputStream(OutputStream out) {
|
||||
this.out = out;
|
||||
this.raf = null;
|
||||
}
|
||||
|
||||
public ZipArchiveOutputStream(File file) throws IOException {
|
||||
OutputStream o = null;
|
||||
RandomAccessFile _raf = null;
|
||||
try {
|
||||
_raf = new RandomAccessFile(file, "rw");
|
||||
_raf.setLength(0L);
|
||||
} catch (IOException e) {
|
||||
IOUtils.closeQuietly(_raf);
|
||||
_raf = null;
|
||||
o = new FileOutputStream(file);
|
||||
}
|
||||
this.out = o;
|
||||
this.raf = _raf;
|
||||
}
|
||||
|
||||
public boolean isSeekable() {
|
||||
return (this.raf != null);
|
||||
}
|
||||
|
||||
public void setEncoding(String encoding) {
|
||||
this.encoding = encoding;
|
||||
this.zipEncoding = ZipEncodingHelper.getZipEncoding(encoding);
|
||||
if (this.useUTF8Flag && !ZipEncodingHelper.isUTF8(encoding))
|
||||
this.useUTF8Flag = false;
|
||||
}
|
||||
|
||||
public String getEncoding() {
|
||||
return this.encoding;
|
||||
}
|
||||
|
||||
public void setUseLanguageEncodingFlag(boolean b) {
|
||||
this.useUTF8Flag = (b && ZipEncodingHelper.isUTF8(this.encoding));
|
||||
}
|
||||
|
||||
public void setCreateUnicodeExtraFields(UnicodeExtraFieldPolicy b) {
|
||||
this.createUnicodeExtraFields = b;
|
||||
}
|
||||
|
||||
public void setFallbackToUTF8(boolean b) {
|
||||
this.fallbackToUTF8 = b;
|
||||
}
|
||||
|
||||
public void setUseZip64(Zip64Mode mode) {
|
||||
this.zip64Mode = mode;
|
||||
}
|
||||
|
||||
public void finish() throws IOException {
|
||||
if (this.finished)
|
||||
throw new IOException("This archive has already been finished");
|
||||
if (this.entry != null)
|
||||
throw new IOException("This archive contains unclosed entries.");
|
||||
this.cdOffset = this.written;
|
||||
for (ZipArchiveEntry ze : this.entries)
|
||||
writeCentralFileHeader(ze);
|
||||
this.cdLength = this.written - this.cdOffset;
|
||||
writeZip64CentralDirectory();
|
||||
writeCentralDirectoryEnd();
|
||||
this.offsets.clear();
|
||||
this.entries.clear();
|
||||
this.def.end();
|
||||
this.finished = true;
|
||||
}
|
||||
|
||||
public void closeArchiveEntry() throws IOException {
|
||||
if (this.finished)
|
||||
throw new IOException("Stream has already been finished");
|
||||
if (this.entry == null)
|
||||
throw new IOException("No current entry to close");
|
||||
if (!this.entry.hasWritten)
|
||||
write(EMPTY, 0, 0);
|
||||
flushDeflater();
|
||||
Zip64Mode effectiveMode = getEffectiveZip64Mode(this.entry.entry);
|
||||
long bytesWritten = this.written - this.entry.dataStart;
|
||||
long realCrc = this.crc.getValue();
|
||||
this.crc.reset();
|
||||
boolean actuallyNeedsZip64 = handleSizesAndCrc(bytesWritten, realCrc, effectiveMode);
|
||||
if (this.raf != null)
|
||||
rewriteSizesAndCrc(actuallyNeedsZip64);
|
||||
writeDataDescriptor(this.entry.entry);
|
||||
this.entry = null;
|
||||
}
|
||||
|
||||
private void flushDeflater() throws IOException {
|
||||
if (this.entry.entry.getMethod() == 8) {
|
||||
this.def.finish();
|
||||
while (!this.def.finished())
|
||||
deflate();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handleSizesAndCrc(long bytesWritten, long crc, Zip64Mode effectiveMode) throws ZipException {
|
||||
if (this.entry.entry.getMethod() == 8) {
|
||||
this.entry.entry.setSize(this.entry.bytesRead);
|
||||
this.entry.entry.setCompressedSize(bytesWritten);
|
||||
this.entry.entry.setCrc(crc);
|
||||
this.def.reset();
|
||||
} else if (this.raf == null) {
|
||||
if (this.entry.entry.getCrc() != crc)
|
||||
throw new ZipException("bad CRC checksum for entry " + this.entry.entry.getName() + ": " + Long.toHexString(this.entry.entry.getCrc()) + " instead of " + Long.toHexString(crc));
|
||||
if (this.entry.entry.getSize() != bytesWritten)
|
||||
throw new ZipException("bad size for entry " + this.entry.entry.getName() + ": " + this.entry.entry.getSize() + " instead of " + bytesWritten);
|
||||
} else {
|
||||
this.entry.entry.setSize(bytesWritten);
|
||||
this.entry.entry.setCompressedSize(bytesWritten);
|
||||
this.entry.entry.setCrc(crc);
|
||||
}
|
||||
boolean actuallyNeedsZip64 = (effectiveMode == Zip64Mode.Always || this.entry.entry.getSize() >= 4294967295L || this.entry.entry.getCompressedSize() >= 4294967295L);
|
||||
if (actuallyNeedsZip64 && effectiveMode == Zip64Mode.Never)
|
||||
throw new Zip64RequiredException(Zip64RequiredException.getEntryTooBigMessage(this.entry.entry));
|
||||
return actuallyNeedsZip64;
|
||||
}
|
||||
|
||||
private void rewriteSizesAndCrc(boolean actuallyNeedsZip64) throws IOException {
|
||||
long save = this.raf.getFilePointer();
|
||||
this.raf.seek(this.entry.localDataStart);
|
||||
writeOut(ZipLong.getBytes(this.entry.entry.getCrc()));
|
||||
if (!hasZip64Extra(this.entry.entry) || !actuallyNeedsZip64) {
|
||||
writeOut(ZipLong.getBytes(this.entry.entry.getCompressedSize()));
|
||||
writeOut(ZipLong.getBytes(this.entry.entry.getSize()));
|
||||
} else {
|
||||
writeOut(ZipLong.ZIP64_MAGIC.getBytes());
|
||||
writeOut(ZipLong.ZIP64_MAGIC.getBytes());
|
||||
}
|
||||
if (hasZip64Extra(this.entry.entry)) {
|
||||
this.raf.seek(this.entry.localDataStart + 12L + 4L + (long)getName(this.entry.entry).limit() + 4L);
|
||||
writeOut(ZipEightByteInteger.getBytes(this.entry.entry.getSize()));
|
||||
writeOut(ZipEightByteInteger.getBytes(this.entry.entry.getCompressedSize()));
|
||||
if (!actuallyNeedsZip64) {
|
||||
this.raf.seek(this.entry.localDataStart - 10L);
|
||||
writeOut(ZipShort.getBytes(10));
|
||||
this.entry.entry.removeExtraField(Zip64ExtendedInformationExtraField.HEADER_ID);
|
||||
this.entry.entry.setExtra();
|
||||
if (this.entry.causedUseOfZip64)
|
||||
this.hasUsedZip64 = false;
|
||||
}
|
||||
}
|
||||
this.raf.seek(save);
|
||||
}
|
||||
|
||||
public void putArchiveEntry(ArchiveEntry archiveEntry) throws IOException {
|
||||
if (this.finished)
|
||||
throw new IOException("Stream has already been finished");
|
||||
if (this.entry != null)
|
||||
closeArchiveEntry();
|
||||
this.entry = new CurrentEntry((ZipArchiveEntry)archiveEntry);
|
||||
this.entries.add(this.entry.entry);
|
||||
setDefaults(this.entry.entry);
|
||||
Zip64Mode effectiveMode = getEffectiveZip64Mode(this.entry.entry);
|
||||
validateSizeInformation(effectiveMode);
|
||||
if (shouldAddZip64Extra(this.entry.entry, effectiveMode)) {
|
||||
Zip64ExtendedInformationExtraField z64 = getZip64Extra(this.entry.entry);
|
||||
ZipEightByteInteger size = ZipEightByteInteger.ZERO;
|
||||
if (this.entry.entry.getMethod() == 0 && this.entry.entry.getSize() != -1L)
|
||||
size = new ZipEightByteInteger(this.entry.entry.getSize());
|
||||
z64.setSize(size);
|
||||
z64.setCompressedSize(size);
|
||||
this.entry.entry.setExtra();
|
||||
}
|
||||
if (this.entry.entry.getMethod() == 8 && this.hasCompressionLevelChanged) {
|
||||
this.def.setLevel(this.level);
|
||||
this.hasCompressionLevelChanged = false;
|
||||
}
|
||||
writeLocalFileHeader(this.entry.entry);
|
||||
}
|
||||
|
||||
private void setDefaults(ZipArchiveEntry entry) {
|
||||
if (entry.getMethod() == -1)
|
||||
entry.setMethod(this.method);
|
||||
if (entry.getTime() == -1L)
|
||||
entry.setTime(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private void validateSizeInformation(Zip64Mode effectiveMode) throws ZipException {
|
||||
if (this.entry.entry.getMethod() == 0 && this.raf == null) {
|
||||
if (this.entry.entry.getSize() == -1L)
|
||||
throw new ZipException("uncompressed size is required for STORED method when not writing to a file");
|
||||
if (this.entry.entry.getCrc() == -1L)
|
||||
throw new ZipException("crc checksum is required for STORED method when not writing to a file");
|
||||
this.entry.entry.setCompressedSize(this.entry.entry.getSize());
|
||||
}
|
||||
if ((this.entry.entry.getSize() >= 4294967295L || this.entry.entry.getCompressedSize() >= 4294967295L) && effectiveMode == Zip64Mode.Never)
|
||||
throw new Zip64RequiredException(Zip64RequiredException.getEntryTooBigMessage(this.entry.entry));
|
||||
}
|
||||
|
||||
private boolean shouldAddZip64Extra(ZipArchiveEntry entry, Zip64Mode mode) {
|
||||
return (mode == Zip64Mode.Always || entry.getSize() >= 4294967295L || entry.getCompressedSize() >= 4294967295L || (entry.getSize() == -1L && this.raf != null && mode != Zip64Mode.Never));
|
||||
}
|
||||
|
||||
public void setComment(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
public void setLevel(int level) {
|
||||
if (level < -1 || level > 9)
|
||||
throw new IllegalArgumentException("Invalid compression level: " + level);
|
||||
this.hasCompressionLevelChanged = (this.level != level);
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public void setMethod(int method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public boolean canWriteEntryData(ArchiveEntry ae) {
|
||||
if (ae instanceof ZipArchiveEntry) {
|
||||
ZipArchiveEntry zae = (ZipArchiveEntry)ae;
|
||||
return (zae.getMethod() != ZipMethod.IMPLODING.getCode() && zae.getMethod() != ZipMethod.UNSHRINKING.getCode() && ZipUtil.canHandleEntryData(zae));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void write(byte[] b, int offset, int length) throws IOException {
|
||||
if (this.entry == null)
|
||||
throw new IllegalStateException("No current entry");
|
||||
ZipUtil.checkRequestedFeatures(this.entry.entry);
|
||||
this.entry.hasWritten = true;
|
||||
if (this.entry.entry.getMethod() == 8) {
|
||||
writeDeflated(b, offset, length);
|
||||
} else {
|
||||
writeOut(b, offset, length);
|
||||
this.written += (long)length;
|
||||
}
|
||||
this.crc.update(b, offset, length);
|
||||
count(length);
|
||||
}
|
||||
|
||||
private void writeDeflated(byte[] b, int offset, int length) throws IOException {
|
||||
if (length > 0 && !this.def.finished()) {
|
||||
this.entry.bytesRead += (long)length;
|
||||
if (length <= 8192) {
|
||||
this.def.setInput(b, offset, length);
|
||||
deflateUntilInputIsNeeded();
|
||||
} else {
|
||||
int fullblocks = length / 8192;
|
||||
for (int i = 0; i < fullblocks; i++) {
|
||||
this.def.setInput(b, offset + i * 8192, 8192);
|
||||
deflateUntilInputIsNeeded();
|
||||
}
|
||||
int done = fullblocks * 8192;
|
||||
if (done < length) {
|
||||
this.def.setInput(b, offset + done, length - done);
|
||||
deflateUntilInputIsNeeded();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (!this.finished)
|
||||
finish();
|
||||
destroy();
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
if (this.out != null)
|
||||
this.out.flush();
|
||||
}
|
||||
|
||||
static final byte[] LFH_SIG = ZipLong.LFH_SIG.getBytes();
|
||||
|
||||
static final byte[] DD_SIG = ZipLong.DD_SIG.getBytes();
|
||||
|
||||
static final byte[] CFH_SIG = ZipLong.CFH_SIG.getBytes();
|
||||
|
||||
static final byte[] EOCD_SIG = ZipLong.getBytes(101010256L);
|
||||
|
||||
static final byte[] ZIP64_EOCD_SIG = ZipLong.getBytes(101075792L);
|
||||
|
||||
static final byte[] ZIP64_EOCD_LOC_SIG = ZipLong.getBytes(117853008L);
|
||||
|
||||
protected final void deflate() throws IOException {
|
||||
int len = this.def.deflate(this.buf, 0, this.buf.length);
|
||||
if (len > 0) {
|
||||
writeOut(this.buf, 0, len);
|
||||
this.written += (long)len;
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeLocalFileHeader(ZipArchiveEntry ze) throws IOException {
|
||||
boolean encodable = this.zipEncoding.canEncode(ze.getName());
|
||||
ByteBuffer name = getName(ze);
|
||||
if (this.createUnicodeExtraFields != UnicodeExtraFieldPolicy.NEVER)
|
||||
addUnicodeExtraFields(ze, encodable, name);
|
||||
this.offsets.put(ze, Long.valueOf(this.written));
|
||||
writeOut(LFH_SIG);
|
||||
this.written += 4L;
|
||||
int zipMethod = ze.getMethod();
|
||||
writeVersionNeededToExtractAndGeneralPurposeBits(zipMethod, (!encodable && this.fallbackToUTF8), hasZip64Extra(ze));
|
||||
this.written += 4L;
|
||||
writeOut(ZipShort.getBytes(zipMethod));
|
||||
this.written += 2L;
|
||||
writeOut(ZipUtil.toDosTime(ze.getTime()));
|
||||
this.written += 4L;
|
||||
this.entry.localDataStart = this.written;
|
||||
if (zipMethod == 8 || this.raf != null) {
|
||||
writeOut(LZERO);
|
||||
if (hasZip64Extra(this.entry.entry)) {
|
||||
writeOut(ZipLong.ZIP64_MAGIC.getBytes());
|
||||
writeOut(ZipLong.ZIP64_MAGIC.getBytes());
|
||||
} else {
|
||||
writeOut(LZERO);
|
||||
writeOut(LZERO);
|
||||
}
|
||||
} else {
|
||||
writeOut(ZipLong.getBytes(ze.getCrc()));
|
||||
byte[] size = ZipLong.ZIP64_MAGIC.getBytes();
|
||||
if (!hasZip64Extra(ze))
|
||||
size = ZipLong.getBytes(ze.getSize());
|
||||
writeOut(size);
|
||||
writeOut(size);
|
||||
}
|
||||
this.written += 12L;
|
||||
writeOut(ZipShort.getBytes(name.limit()));
|
||||
this.written += 2L;
|
||||
byte[] extra = ze.getLocalFileDataExtra();
|
||||
writeOut(ZipShort.getBytes(extra.length));
|
||||
this.written += 2L;
|
||||
writeOut(name.array(), name.arrayOffset(), name.limit() - name.position());
|
||||
this.written += (long)name.limit();
|
||||
writeOut(extra);
|
||||
this.written += (long)extra.length;
|
||||
this.entry.dataStart = this.written;
|
||||
}
|
||||
|
||||
private void addUnicodeExtraFields(ZipArchiveEntry ze, boolean encodable, ByteBuffer name) throws IOException {
|
||||
if (this.createUnicodeExtraFields == UnicodeExtraFieldPolicy.ALWAYS || !encodable)
|
||||
ze.addExtraField(new UnicodePathExtraField(ze.getName(), name.array(), name.arrayOffset(), name.limit() - name.position()));
|
||||
String comm = ze.getComment();
|
||||
if (comm != null && !"".equals(comm)) {
|
||||
boolean commentEncodable = this.zipEncoding.canEncode(comm);
|
||||
if (this.createUnicodeExtraFields == UnicodeExtraFieldPolicy.ALWAYS || !commentEncodable) {
|
||||
ByteBuffer commentB = getEntryEncoding(ze).encode(comm);
|
||||
ze.addExtraField(new UnicodeCommentExtraField(comm, commentB.array(), commentB.arrayOffset(), commentB.limit() - commentB.position()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeDataDescriptor(ZipArchiveEntry ze) throws IOException {
|
||||
if (ze.getMethod() != 8 || this.raf != null)
|
||||
return;
|
||||
writeOut(DD_SIG);
|
||||
writeOut(ZipLong.getBytes(ze.getCrc()));
|
||||
int sizeFieldSize = 4;
|
||||
if (!hasZip64Extra(ze)) {
|
||||
writeOut(ZipLong.getBytes(ze.getCompressedSize()));
|
||||
writeOut(ZipLong.getBytes(ze.getSize()));
|
||||
} else {
|
||||
sizeFieldSize = 8;
|
||||
writeOut(ZipEightByteInteger.getBytes(ze.getCompressedSize()));
|
||||
writeOut(ZipEightByteInteger.getBytes(ze.getSize()));
|
||||
}
|
||||
this.written += (long)(8 + 2 * sizeFieldSize);
|
||||
}
|
||||
|
||||
protected void writeCentralFileHeader(ZipArchiveEntry ze) throws IOException {
|
||||
writeOut(CFH_SIG);
|
||||
this.written += 4L;
|
||||
long lfhOffset = this.offsets.get(ze);
|
||||
boolean needsZip64Extra = (hasZip64Extra(ze) || ze.getCompressedSize() >= 4294967295L || ze.getSize() >= 4294967295L || lfhOffset >= 4294967295L);
|
||||
if (needsZip64Extra && this.zip64Mode == Zip64Mode.Never)
|
||||
throw new Zip64RequiredException("archive's size exceeds the limit of 4GByte.");
|
||||
handleZip64Extra(ze, lfhOffset, needsZip64Extra);
|
||||
writeOut(ZipShort.getBytes(ze.getPlatform() << 8 | (!this.hasUsedZip64 ? 20 : 45)));
|
||||
this.written += 2L;
|
||||
int zipMethod = ze.getMethod();
|
||||
boolean encodable = this.zipEncoding.canEncode(ze.getName());
|
||||
writeVersionNeededToExtractAndGeneralPurposeBits(zipMethod, (!encodable && this.fallbackToUTF8), needsZip64Extra);
|
||||
this.written += 4L;
|
||||
writeOut(ZipShort.getBytes(zipMethod));
|
||||
this.written += 2L;
|
||||
writeOut(ZipUtil.toDosTime(ze.getTime()));
|
||||
this.written += 4L;
|
||||
writeOut(ZipLong.getBytes(ze.getCrc()));
|
||||
if (ze.getCompressedSize() >= 4294967295L || ze.getSize() >= 4294967295L) {
|
||||
writeOut(ZipLong.ZIP64_MAGIC.getBytes());
|
||||
writeOut(ZipLong.ZIP64_MAGIC.getBytes());
|
||||
} else {
|
||||
writeOut(ZipLong.getBytes(ze.getCompressedSize()));
|
||||
writeOut(ZipLong.getBytes(ze.getSize()));
|
||||
}
|
||||
this.written += 12L;
|
||||
ByteBuffer name = getName(ze);
|
||||
writeOut(ZipShort.getBytes(name.limit()));
|
||||
this.written += 2L;
|
||||
byte[] extra = ze.getCentralDirectoryExtra();
|
||||
writeOut(ZipShort.getBytes(extra.length));
|
||||
this.written += 2L;
|
||||
String comm = ze.getComment();
|
||||
if (comm == null)
|
||||
comm = "";
|
||||
ByteBuffer commentB = getEntryEncoding(ze).encode(comm);
|
||||
writeOut(ZipShort.getBytes(commentB.limit()));
|
||||
this.written += 2L;
|
||||
writeOut(ZERO);
|
||||
this.written += 2L;
|
||||
writeOut(ZipShort.getBytes(ze.getInternalAttributes()));
|
||||
this.written += 2L;
|
||||
writeOut(ZipLong.getBytes(ze.getExternalAttributes()));
|
||||
this.written += 4L;
|
||||
writeOut(ZipLong.getBytes(Math.min(lfhOffset, 4294967295L)));
|
||||
this.written += 4L;
|
||||
writeOut(name.array(), name.arrayOffset(), name.limit() - name.position());
|
||||
this.written += (long)name.limit();
|
||||
writeOut(extra);
|
||||
this.written += (long)extra.length;
|
||||
writeOut(commentB.array(), commentB.arrayOffset(), commentB.limit() - commentB.position());
|
||||
this.written += (long)commentB.limit();
|
||||
}
|
||||
|
||||
private void handleZip64Extra(ZipArchiveEntry ze, long lfhOffset, boolean needsZip64Extra) {
|
||||
if (needsZip64Extra) {
|
||||
Zip64ExtendedInformationExtraField z64 = getZip64Extra(ze);
|
||||
if (ze.getCompressedSize() >= 4294967295L || ze.getSize() >= 4294967295L) {
|
||||
z64.setCompressedSize(new ZipEightByteInteger(ze.getCompressedSize()));
|
||||
z64.setSize(new ZipEightByteInteger(ze.getSize()));
|
||||
} else {
|
||||
z64.setCompressedSize(null);
|
||||
z64.setSize(null);
|
||||
}
|
||||
if (lfhOffset >= 4294967295L)
|
||||
z64.setRelativeHeaderOffset(new ZipEightByteInteger(lfhOffset));
|
||||
ze.setExtra();
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeCentralDirectoryEnd() throws IOException {
|
||||
writeOut(EOCD_SIG);
|
||||
writeOut(ZERO);
|
||||
writeOut(ZERO);
|
||||
int numberOfEntries = this.entries.size();
|
||||
if (numberOfEntries > 65535 && this.zip64Mode == Zip64Mode.Never)
|
||||
throw new Zip64RequiredException("archive contains more than 65535 entries.");
|
||||
if (this.cdOffset > 4294967295L && this.zip64Mode == Zip64Mode.Never)
|
||||
throw new Zip64RequiredException("archive's size exceeds the limit of 4GByte.");
|
||||
byte[] num = ZipShort.getBytes(Math.min(numberOfEntries, 65535));
|
||||
writeOut(num);
|
||||
writeOut(num);
|
||||
writeOut(ZipLong.getBytes(Math.min(this.cdLength, 4294967295L)));
|
||||
writeOut(ZipLong.getBytes(Math.min(this.cdOffset, 4294967295L)));
|
||||
ByteBuffer data = this.zipEncoding.encode(this.comment);
|
||||
writeOut(ZipShort.getBytes(data.limit()));
|
||||
writeOut(data.array(), data.arrayOffset(), data.limit() - data.position());
|
||||
}
|
||||
|
||||
private static final byte[] ONE = ZipLong.getBytes(1L);
|
||||
|
||||
protected void writeZip64CentralDirectory() throws IOException {
|
||||
if (this.zip64Mode == Zip64Mode.Never)
|
||||
return;
|
||||
if (!this.hasUsedZip64 && (this.cdOffset >= 4294967295L || this.cdLength >= 4294967295L || this.entries.size() >= 65535))
|
||||
this.hasUsedZip64 = true;
|
||||
if (!this.hasUsedZip64)
|
||||
return;
|
||||
long offset = this.written;
|
||||
writeOut(ZIP64_EOCD_SIG);
|
||||
writeOut(ZipEightByteInteger.getBytes(44L));
|
||||
writeOut(ZipShort.getBytes(45));
|
||||
writeOut(ZipShort.getBytes(45));
|
||||
writeOut(LZERO);
|
||||
writeOut(LZERO);
|
||||
byte[] num = ZipEightByteInteger.getBytes((long)this.entries.size());
|
||||
writeOut(num);
|
||||
writeOut(num);
|
||||
writeOut(ZipEightByteInteger.getBytes(this.cdLength));
|
||||
writeOut(ZipEightByteInteger.getBytes(this.cdOffset));
|
||||
writeOut(ZIP64_EOCD_LOC_SIG);
|
||||
writeOut(LZERO);
|
||||
writeOut(ZipEightByteInteger.getBytes(offset));
|
||||
writeOut(ONE);
|
||||
}
|
||||
|
||||
protected final void writeOut(byte[] data) throws IOException {
|
||||
writeOut(data, 0, data.length);
|
||||
}
|
||||
|
||||
protected final void writeOut(byte[] data, int offset, int length) throws IOException {
|
||||
if (this.raf != null) {
|
||||
this.raf.write(data, offset, length);
|
||||
} else {
|
||||
this.out.write(data, offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
private void deflateUntilInputIsNeeded() throws IOException {
|
||||
while (!this.def.needsInput())
|
||||
deflate();
|
||||
}
|
||||
|
||||
private void writeVersionNeededToExtractAndGeneralPurposeBits(int zipMethod, boolean utfFallback, boolean zip64) throws IOException {
|
||||
int versionNeededToExtract = 10;
|
||||
GeneralPurposeBit b = new GeneralPurposeBit();
|
||||
b.useUTF8ForNames((this.useUTF8Flag || utfFallback));
|
||||
if (zipMethod == 8 && this.raf == null) {
|
||||
versionNeededToExtract = 20;
|
||||
b.useDataDescriptor(true);
|
||||
}
|
||||
if (zip64)
|
||||
versionNeededToExtract = 45;
|
||||
writeOut(ZipShort.getBytes(versionNeededToExtract));
|
||||
writeOut(b.encode());
|
||||
}
|
||||
|
||||
public ArchiveEntry createArchiveEntry(File inputFile, String entryName) throws IOException {
|
||||
if (this.finished)
|
||||
throw new IOException("Stream has already been finished");
|
||||
return new ZipArchiveEntry(inputFile, entryName);
|
||||
}
|
||||
|
||||
private Zip64ExtendedInformationExtraField getZip64Extra(ZipArchiveEntry ze) {
|
||||
if (this.entry != null)
|
||||
this.entry.causedUseOfZip64 = !this.hasUsedZip64;
|
||||
this.hasUsedZip64 = true;
|
||||
Zip64ExtendedInformationExtraField z64 = (Zip64ExtendedInformationExtraField)ze.getExtraField(Zip64ExtendedInformationExtraField.HEADER_ID);
|
||||
if (z64 == null)
|
||||
z64 = new Zip64ExtendedInformationExtraField();
|
||||
ze.addAsFirstExtraField(z64);
|
||||
return z64;
|
||||
}
|
||||
|
||||
private boolean hasZip64Extra(ZipArchiveEntry ze) {
|
||||
return (ze.getExtraField(Zip64ExtendedInformationExtraField.HEADER_ID) != null);
|
||||
}
|
||||
|
||||
private Zip64Mode getEffectiveZip64Mode(ZipArchiveEntry ze) {
|
||||
if (this.zip64Mode != Zip64Mode.AsNeeded || this.raf != null || ze.getMethod() != 8 || ze.getSize() != -1L)
|
||||
return this.zip64Mode;
|
||||
return Zip64Mode.Never;
|
||||
}
|
||||
|
||||
private ZipEncoding getEntryEncoding(ZipArchiveEntry ze) {
|
||||
boolean encodable = this.zipEncoding.canEncode(ze.getName());
|
||||
return (!encodable && this.fallbackToUTF8) ? ZipEncodingHelper.UTF8_ZIP_ENCODING : this.zipEncoding;
|
||||
}
|
||||
|
||||
private ByteBuffer getName(ZipArchiveEntry ze) throws IOException {
|
||||
return getEntryEncoding(ze).encode(ze.getName());
|
||||
}
|
||||
|
||||
void destroy() throws IOException {
|
||||
if (this.raf != null)
|
||||
this.raf.close();
|
||||
if (this.out != null)
|
||||
this.out.close();
|
||||
}
|
||||
|
||||
public static final class UnicodeExtraFieldPolicy {
|
||||
public static final UnicodeExtraFieldPolicy ALWAYS = new UnicodeExtraFieldPolicy("always");
|
||||
|
||||
public static final UnicodeExtraFieldPolicy NEVER = new UnicodeExtraFieldPolicy("never");
|
||||
|
||||
public static final UnicodeExtraFieldPolicy NOT_ENCODEABLE = new UnicodeExtraFieldPolicy("not encodeable");
|
||||
|
||||
private final String name;
|
||||
|
||||
private UnicodeExtraFieldPolicy(String n) {
|
||||
this.name = n;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CurrentEntry {
|
||||
private final ZipArchiveEntry entry;
|
||||
|
||||
private CurrentEntry(ZipArchiveEntry entry) {
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
private long localDataStart = 0L;
|
||||
|
||||
private long dataStart = 0L;
|
||||
|
||||
private long bytesRead = 0L;
|
||||
|
||||
private boolean causedUseOfZip64 = false;
|
||||
|
||||
private boolean hasWritten;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
final class ZipConstants {
|
||||
static final int BYTE_MASK = 255;
|
||||
|
||||
static final int SHORT = 2;
|
||||
|
||||
static final int WORD = 4;
|
||||
|
||||
static final int DWORD = 8;
|
||||
|
||||
static final int INITIAL_VERSION = 10;
|
||||
|
||||
static final int DATA_DESCRIPTOR_MIN_VERSION = 20;
|
||||
|
||||
static final int ZIP64_MIN_VERSION = 45;
|
||||
|
||||
static final int ZIP64_MAGIC_SHORT = 65535;
|
||||
|
||||
static final long ZIP64_MAGIC = 4294967295L;
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
|
||||
public final class ZipEightByteInteger implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final int BYTE_1 = 1;
|
||||
|
||||
private static final int BYTE_1_MASK = 65280;
|
||||
|
||||
private static final int BYTE_1_SHIFT = 8;
|
||||
|
||||
private static final int BYTE_2 = 2;
|
||||
|
||||
private static final int BYTE_2_MASK = 16711680;
|
||||
|
||||
private static final int BYTE_2_SHIFT = 16;
|
||||
|
||||
private static final int BYTE_3 = 3;
|
||||
|
||||
private static final long BYTE_3_MASK = 4278190080L;
|
||||
|
||||
private static final int BYTE_3_SHIFT = 24;
|
||||
|
||||
private static final int BYTE_4 = 4;
|
||||
|
||||
private static final long BYTE_4_MASK = 1095216660480L;
|
||||
|
||||
private static final int BYTE_4_SHIFT = 32;
|
||||
|
||||
private static final int BYTE_5 = 5;
|
||||
|
||||
private static final long BYTE_5_MASK = 280375465082880L;
|
||||
|
||||
private static final int BYTE_5_SHIFT = 40;
|
||||
|
||||
private static final int BYTE_6 = 6;
|
||||
|
||||
private static final long BYTE_6_MASK = 71776119061217280L;
|
||||
|
||||
private static final int BYTE_6_SHIFT = 48;
|
||||
|
||||
private static final int BYTE_7 = 7;
|
||||
|
||||
private static final long BYTE_7_MASK = 9151314442816847872L;
|
||||
|
||||
private static final int BYTE_7_SHIFT = 56;
|
||||
|
||||
private static final int LEFTMOST_BIT_SHIFT = 63;
|
||||
|
||||
private static final byte LEFTMOST_BIT = -128;
|
||||
|
||||
private final BigInteger value;
|
||||
|
||||
public static final ZipEightByteInteger ZERO = new ZipEightByteInteger(0L);
|
||||
|
||||
public ZipEightByteInteger(long value) {
|
||||
this(BigInteger.valueOf(value));
|
||||
}
|
||||
|
||||
public ZipEightByteInteger(BigInteger value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public ZipEightByteInteger(byte[] bytes) {
|
||||
this(bytes, 0);
|
||||
}
|
||||
|
||||
public ZipEightByteInteger(byte[] bytes, int offset) {
|
||||
this.value = getValue(bytes, offset);
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return getBytes(this.value);
|
||||
}
|
||||
|
||||
public long getLongValue() {
|
||||
return this.value.longValue();
|
||||
}
|
||||
|
||||
public BigInteger getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public static byte[] getBytes(long value) {
|
||||
return getBytes(BigInteger.valueOf(value));
|
||||
}
|
||||
|
||||
public static byte[] getBytes(BigInteger value) {
|
||||
byte[] result = new byte[8];
|
||||
long val = value.longValue();
|
||||
result[0] = (byte)(int)(val & 0xFFL);
|
||||
result[1] = (byte)(int)((val & 0xFF00L) >> 8L);
|
||||
result[2] = (byte)(int)((val & 0xFF0000L) >> 16L);
|
||||
result[3] = (byte)(int)((val & 0xFF000000L) >> 24L);
|
||||
result[4] = (byte)(int)((val & 0xFF00000000L) >> 32L);
|
||||
result[5] = (byte)(int)((val & 0xFF0000000000L) >> 40L);
|
||||
result[6] = (byte)(int)((val & 0xFF000000000000L) >> 48L);
|
||||
result[7] = (byte)(int)((val & 0x7F00000000000000L) >> 56L);
|
||||
if (value.testBit(63))
|
||||
result[7] = (byte)(result[7] | Byte.MIN_VALUE);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static long getLongValue(byte[] bytes, int offset) {
|
||||
return getValue(bytes, offset).longValue();
|
||||
}
|
||||
|
||||
public static BigInteger getValue(byte[] bytes, int offset) {
|
||||
long value = (long)bytes[offset + 7] << 56L & 0x7F00000000000000L;
|
||||
value += (long)bytes[offset + 6] << 48L & 0xFF000000000000L;
|
||||
value += (long)bytes[offset + 5] << 40L & 0xFF0000000000L;
|
||||
value += (long)bytes[offset + 4] << 32L & 0xFF00000000L;
|
||||
value += (long)bytes[offset + 3] << 24L & 0xFF000000L;
|
||||
value += (long)bytes[offset + 2] << 16L & 0xFF0000L;
|
||||
value += (long)bytes[offset + 1] << 8L & 0xFF00L;
|
||||
value += (long)bytes[offset] & 0xFFL;
|
||||
BigInteger val = BigInteger.valueOf(value);
|
||||
return ((bytes[offset + 7] & Byte.MIN_VALUE) == -128) ? val.setBit(63) : val;
|
||||
}
|
||||
|
||||
public static long getLongValue(byte[] bytes) {
|
||||
return getLongValue(bytes, 0);
|
||||
}
|
||||
|
||||
public static BigInteger getValue(byte[] bytes) {
|
||||
return getValue(bytes, 0);
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || !(o instanceof ZipEightByteInteger))
|
||||
return false;
|
||||
return this.value.equals(((ZipEightByteInteger)o).getValue());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return this.value.hashCode();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "ZipEightByteInteger value: " + this.value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public interface ZipEncoding {
|
||||
boolean canEncode(String paramString);
|
||||
|
||||
ByteBuffer encode(String paramString) throws IOException;
|
||||
|
||||
String decode(byte[] paramArrayOfbyte) throws IOException;
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.UnsupportedCharsetException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.apache.commons.compress.utils.Charsets;
|
||||
|
||||
public abstract class ZipEncodingHelper {
|
||||
private static final Map<String, SimpleEncodingHolder> simpleEncodings;
|
||||
|
||||
private static class SimpleEncodingHolder {
|
||||
private final char[] highChars;
|
||||
|
||||
private Simple8BitZipEncoding encoding;
|
||||
|
||||
SimpleEncodingHolder(char[] highChars) {
|
||||
this.highChars = highChars;
|
||||
}
|
||||
|
||||
public synchronized Simple8BitZipEncoding getEncoding() {
|
||||
if (this.encoding == null)
|
||||
this.encoding = new Simple8BitZipEncoding(this.highChars);
|
||||
return this.encoding;
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
Map<String, SimpleEncodingHolder> se = new HashMap<String, SimpleEncodingHolder>();
|
||||
char[] cp437_high_chars = {
|
||||
'Ç', 'ü', 'é', 'â', 'ä', 'à', 'å', 'ç', 'ê', 'ë',
|
||||
'è', 'ï', 'î', 'ì', 'Ä', 'Å', 'É', 'æ', 'Æ', 'ô',
|
||||
'ö', 'ò', 'û', 'ù', 'ÿ', 'Ö', 'Ü', '¢', '£', '¥',
|
||||
'₧', 'ƒ', 'á', 'í', 'ó', 'ú', 'ñ', 'Ñ', 'ª', 'º',
|
||||
'¿', '⌐', '¬', '½', '¼', '¡', '«', '»', '░', '▒',
|
||||
'▓', '│', '┤', '╡', '╢', '╖', '╕', '╣', '║', '╗',
|
||||
'╝', '╜', '╛', '┐', '└', '┴', '┬', '├', '─', '┼',
|
||||
'╞', '╟', '╚', '╔', '╩', '╦', '╠', '═', '╬', '╧',
|
||||
'╨', '╤', '╥', '╙', '╘', '╒', '╓', '╫', '╪', '┘',
|
||||
'┌', '█', '▄', '▌', '▐', '▀', 'α', 'ß', 'Γ', 'π',
|
||||
'Σ', 'σ', 'µ', 'τ', 'Φ', 'Θ', 'Ω', 'δ', '∞', 'φ',
|
||||
'ε', '∩', '≡', '±', '≥', '≤', '⌠', '⌡', '÷', '≈',
|
||||
'°', '∙', '·', '√', 'ⁿ', '²', '■', '\u00A0' };
|
||||
SimpleEncodingHolder cp437 = new SimpleEncodingHolder(cp437_high_chars);
|
||||
se.put("CP437", cp437);
|
||||
se.put("Cp437", cp437);
|
||||
se.put("cp437", cp437);
|
||||
se.put("IBM437", cp437);
|
||||
se.put("ibm437", cp437);
|
||||
char[] cp850_high_chars = {
|
||||
'Ç', 'ü', 'é', 'â', 'ä', 'à', 'å', 'ç', 'ê', 'ë',
|
||||
'è', 'ï', 'î', 'ì', 'Ä', 'Å', 'É', 'æ', 'Æ', 'ô',
|
||||
'ö', 'ò', 'û', 'ù', 'ÿ', 'Ö', 'Ü', 'ø', '£', 'Ø',
|
||||
'×', 'ƒ', 'á', 'í', 'ó', 'ú', 'ñ', 'Ñ', 'ª', 'º',
|
||||
'¿', '®', '¬', '½', '¼', '¡', '«', '»', '░', '▒',
|
||||
'▓', '│', '┤', 'Á', 'Â', 'À', '©', '╣', '║', '╗',
|
||||
'╝', '¢', '¥', '┐', '└', '┴', '┬', '├', '─', '┼',
|
||||
'ã', 'Ã', '╚', '╔', '╩', '╦', '╠', '═', '╬', '¤',
|
||||
'ð', 'Ð', 'Ê', 'Ë', 'È', 'ı', 'Í', 'Î', 'Ï', '┘',
|
||||
'┌', '█', '▄', '¦', 'Ì', '▀', 'Ó', 'ß', 'Ô', 'Ò',
|
||||
'õ', 'Õ', 'µ', 'þ', 'Þ', 'Ú', 'Û', 'Ù', 'ý', 'Ý',
|
||||
'¯', '´', '\u00AD', '±', '‗', '¾', '¶', '§', '÷', '¸',
|
||||
'°', '¨', '·', '¹', '³', '²', '■', '\u00A0' };
|
||||
SimpleEncodingHolder cp850 = new SimpleEncodingHolder(cp850_high_chars);
|
||||
se.put("CP850", cp850);
|
||||
se.put("Cp850", cp850);
|
||||
se.put("cp850", cp850);
|
||||
se.put("IBM850", cp850);
|
||||
se.put("ibm850", cp850);
|
||||
simpleEncodings = Collections.<String, SimpleEncodingHolder>unmodifiableMap(se);
|
||||
}
|
||||
|
||||
static ByteBuffer growBuffer(ByteBuffer b, int newCapacity) {
|
||||
b.limit(b.position());
|
||||
b.rewind();
|
||||
int c2 = b.capacity() * 2;
|
||||
ByteBuffer on = ByteBuffer.allocate((c2 < newCapacity) ? newCapacity : c2);
|
||||
on.put(b);
|
||||
return on;
|
||||
}
|
||||
|
||||
private static final byte[] HEX_DIGITS = new byte[] {
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
|
||||
65, 66, 67, 68, 69, 70 };
|
||||
|
||||
static final String UTF8 = "UTF8";
|
||||
|
||||
static void appendSurrogate(ByteBuffer bb, char c) {
|
||||
bb.put((byte)37);
|
||||
bb.put((byte)85);
|
||||
bb.put(HEX_DIGITS[c >> 12 & 0xF]);
|
||||
bb.put(HEX_DIGITS[c >> 8 & 0xF]);
|
||||
bb.put(HEX_DIGITS[c >> 4 & 0xF]);
|
||||
bb.put(HEX_DIGITS[c & 0xF]);
|
||||
}
|
||||
|
||||
static final ZipEncoding UTF8_ZIP_ENCODING = new FallbackZipEncoding("UTF8");
|
||||
|
||||
public static ZipEncoding getZipEncoding(String name) {
|
||||
if (isUTF8(name))
|
||||
return UTF8_ZIP_ENCODING;
|
||||
if (name == null)
|
||||
return new FallbackZipEncoding();
|
||||
SimpleEncodingHolder h = simpleEncodings.get(name);
|
||||
if (h != null)
|
||||
return h.getEncoding();
|
||||
try {
|
||||
Charset cs = Charset.forName(name);
|
||||
return new NioZipEncoding(cs);
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
return new FallbackZipEncoding(name);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isUTF8(String charsetName) {
|
||||
if (charsetName == null)
|
||||
charsetName = System.getProperty("file.encoding");
|
||||
if (Charsets.UTF_8.name().equalsIgnoreCase(charsetName))
|
||||
return true;
|
||||
for (String alias : Charsets.UTF_8.aliases()) {
|
||||
if (alias.equalsIgnoreCase(charsetName))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
public interface ZipExtraField {
|
||||
ZipShort getHeaderId();
|
||||
|
||||
ZipShort getLocalFileDataLength();
|
||||
|
||||
ZipShort getCentralDirectoryLength();
|
||||
|
||||
byte[] getLocalFileDataData();
|
||||
|
||||
byte[] getCentralDirectoryData();
|
||||
|
||||
void parseFromLocalFileData(byte[] paramArrayOfbyte, int paramInt1, int paramInt2) throws ZipException;
|
||||
|
||||
void parseFromCentralDirectoryData(byte[] paramArrayOfbyte, int paramInt1, int paramInt2) throws ZipException;
|
||||
}
|
||||
|
|
@ -0,0 +1,540 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.Inflater;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
import java.util.zip.ZipException;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
|
||||
public class ZipFile implements Closeable {
|
||||
private static final int HASH_SIZE = 509;
|
||||
|
||||
static final int NIBLET_MASK = 15;
|
||||
|
||||
static final int BYTE_SHIFT = 8;
|
||||
|
||||
private static final int POS_0 = 0;
|
||||
|
||||
private static final int POS_1 = 1;
|
||||
|
||||
private static final int POS_2 = 2;
|
||||
|
||||
private static final int POS_3 = 3;
|
||||
|
||||
private final List<ZipArchiveEntry> entries = new LinkedList<ZipArchiveEntry>();
|
||||
|
||||
private final Map<String, LinkedList<ZipArchiveEntry>> nameMap = new HashMap<String, LinkedList<ZipArchiveEntry>>(509);
|
||||
|
||||
private final String encoding;
|
||||
|
||||
private final ZipEncoding zipEncoding;
|
||||
|
||||
private final String archiveName;
|
||||
|
||||
private final RandomAccessFile archive;
|
||||
|
||||
private final boolean useUnicodeExtraFields;
|
||||
|
||||
private boolean closed;
|
||||
|
||||
private static final class OffsetEntry {
|
||||
private OffsetEntry() {}
|
||||
|
||||
private long headerOffset = -1L;
|
||||
|
||||
private long dataOffset = -1L;
|
||||
}
|
||||
|
||||
private final byte[] DWORD_BUF = new byte[8];
|
||||
|
||||
private final byte[] WORD_BUF = new byte[4];
|
||||
|
||||
private final byte[] CFH_BUF = new byte[42];
|
||||
|
||||
private final byte[] SHORT_BUF = new byte[2];
|
||||
|
||||
private static final int CFH_LEN = 42;
|
||||
|
||||
public ZipFile(File f) throws IOException {
|
||||
this(f, "UTF8");
|
||||
}
|
||||
|
||||
public ZipFile(String name) throws IOException {
|
||||
this(new File(name), "UTF8");
|
||||
}
|
||||
|
||||
public ZipFile(String name, String encoding) throws IOException {
|
||||
this(new File(name), encoding, true);
|
||||
}
|
||||
|
||||
public ZipFile(File f, String encoding) throws IOException {
|
||||
this(f, encoding, true);
|
||||
}
|
||||
|
||||
public ZipFile(File f, String encoding, boolean useUnicodeExtraFields) throws IOException {
|
||||
this.archiveName = f.getAbsolutePath();
|
||||
this.encoding = encoding;
|
||||
this.zipEncoding = ZipEncodingHelper.getZipEncoding(encoding);
|
||||
this.useUnicodeExtraFields = useUnicodeExtraFields;
|
||||
this.archive = new RandomAccessFile(f, "r");
|
||||
boolean success = false;
|
||||
try {
|
||||
Map<ZipArchiveEntry, NameAndComment> entriesWithoutUTF8Flag = populateFromCentralDirectory();
|
||||
resolveLocalFileHeaderData(entriesWithoutUTF8Flag);
|
||||
success = true;
|
||||
} finally {
|
||||
if (!success) {
|
||||
this.closed = true;
|
||||
IOUtils.closeQuietly(this.archive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getEncoding() {
|
||||
return this.encoding;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
this.closed = true;
|
||||
this.archive.close();
|
||||
}
|
||||
|
||||
public static void closeQuietly(ZipFile zipfile) {
|
||||
IOUtils.closeQuietly(zipfile);
|
||||
}
|
||||
|
||||
public Enumeration<ZipArchiveEntry> getEntries() {
|
||||
return Collections.<ZipArchiveEntry>enumeration(this.entries);
|
||||
}
|
||||
|
||||
public Enumeration<ZipArchiveEntry> getEntriesInPhysicalOrder() {
|
||||
ZipArchiveEntry[] allEntries = this.entries.<ZipArchiveEntry>toArray(new ZipArchiveEntry[0]);
|
||||
Arrays.<ZipArchiveEntry>sort(allEntries, this.OFFSET_COMPARATOR);
|
||||
return Collections.<ZipArchiveEntry>enumeration(Arrays.<ZipArchiveEntry>asList(allEntries));
|
||||
}
|
||||
|
||||
public ZipArchiveEntry getEntry(String name) {
|
||||
LinkedList<ZipArchiveEntry> entriesOfThatName = this.nameMap.get(name);
|
||||
return (entriesOfThatName != null) ? entriesOfThatName.getFirst() : null;
|
||||
}
|
||||
|
||||
public Iterable<ZipArchiveEntry> getEntries(String name) {
|
||||
List<ZipArchiveEntry> entriesOfThatName = this.nameMap.get(name);
|
||||
return (entriesOfThatName != null) ? entriesOfThatName : Collections.<ZipArchiveEntry>emptyList();
|
||||
}
|
||||
|
||||
public Iterable<ZipArchiveEntry> getEntriesInPhysicalOrder(String name) {
|
||||
ZipArchiveEntry[] entriesOfThatName = new ZipArchiveEntry[0];
|
||||
if (this.nameMap.containsKey(name)) {
|
||||
entriesOfThatName = (ZipArchiveEntry[])this.nameMap.get(name).toArray(entriesOfThatName);
|
||||
Arrays.<ZipArchiveEntry>sort(entriesOfThatName, this.OFFSET_COMPARATOR);
|
||||
}
|
||||
return Arrays.<ZipArchiveEntry>asList(entriesOfThatName);
|
||||
}
|
||||
|
||||
public boolean canReadEntryData(ZipArchiveEntry ze) {
|
||||
return ZipUtil.canHandleEntryData(ze);
|
||||
}
|
||||
|
||||
public InputStream getInputStream(ZipArchiveEntry ze) throws IOException, ZipException {
|
||||
final Inflater inflater;
|
||||
if (!(ze instanceof Entry))
|
||||
return null;
|
||||
OffsetEntry offsetEntry = ((Entry)ze).getOffsetEntry();
|
||||
ZipUtil.checkRequestedFeatures(ze);
|
||||
long start = offsetEntry.dataOffset;
|
||||
BoundedInputStream bis = new BoundedInputStream(start, ze.getCompressedSize());
|
||||
switch (ZipMethod.getMethodByCode(ze.getMethod())) {
|
||||
case STORED:
|
||||
return bis;
|
||||
case UNSHRINKING:
|
||||
return new UnshrinkingInputStream(bis);
|
||||
case IMPLODING:
|
||||
return new ExplodingInputStream(ze.getGeneralPurposeBit().getSlidingDictionarySize(), ze.getGeneralPurposeBit().getNumberOfShannonFanoTrees(), new BufferedInputStream(bis));
|
||||
case DEFLATED:
|
||||
bis.addDummy();
|
||||
inflater = new Inflater(true);
|
||||
return new InflaterInputStream(bis, inflater) {
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
inflater.end();
|
||||
}
|
||||
};
|
||||
}
|
||||
throw new ZipException("Found unsupported compression method " + ze.getMethod());
|
||||
}
|
||||
|
||||
public String getUnixSymlink(ZipArchiveEntry entry) throws IOException {
|
||||
if (entry != null && entry.isUnixSymlink()) {
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = getInputStream(entry);
|
||||
byte[] symlinkBytes = IOUtils.toByteArray(in);
|
||||
return this.zipEncoding.decode(symlinkBytes);
|
||||
} finally {
|
||||
if (in != null)
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void finalize() throws Throwable {
|
||||
try {
|
||||
if (!this.closed) {
|
||||
System.err.println("Cleaning up unclosed ZipFile for archive " + this.archiveName);
|
||||
close();
|
||||
}
|
||||
} finally {
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
private static final long CFH_SIG = ZipLong.getValue(ZipArchiveOutputStream.CFH_SIG);
|
||||
|
||||
static final int MIN_EOCD_SIZE = 22;
|
||||
|
||||
private static final int MAX_EOCD_SIZE = 65557;
|
||||
|
||||
private static final int CFD_LOCATOR_OFFSET = 16;
|
||||
|
||||
private static final int ZIP64_EOCDL_LENGTH = 20;
|
||||
|
||||
private static final int ZIP64_EOCDL_LOCATOR_OFFSET = 8;
|
||||
|
||||
private static final int ZIP64_EOCD_CFD_LOCATOR_OFFSET = 48;
|
||||
|
||||
private static final long LFH_OFFSET_FOR_FILENAME_LENGTH = 26L;
|
||||
|
||||
private Map<ZipArchiveEntry, NameAndComment> populateFromCentralDirectory() throws IOException {
|
||||
HashMap<ZipArchiveEntry, NameAndComment> noUTF8Flag = new HashMap<ZipArchiveEntry, NameAndComment>();
|
||||
positionAtCentralDirectory();
|
||||
this.archive.readFully(this.WORD_BUF);
|
||||
long sig = ZipLong.getValue(this.WORD_BUF);
|
||||
if (sig != CFH_SIG && startsWithLocalFileHeader())
|
||||
throw new IOException("central directory is empty, can't expand corrupt archive.");
|
||||
while (sig == CFH_SIG) {
|
||||
readCentralDirectoryEntry(noUTF8Flag);
|
||||
this.archive.readFully(this.WORD_BUF);
|
||||
sig = ZipLong.getValue(this.WORD_BUF);
|
||||
}
|
||||
return noUTF8Flag;
|
||||
}
|
||||
|
||||
private void readCentralDirectoryEntry(Map<ZipArchiveEntry, NameAndComment> noUTF8Flag) throws IOException {
|
||||
this.archive.readFully(this.CFH_BUF);
|
||||
int off = 0;
|
||||
OffsetEntry offset = new OffsetEntry();
|
||||
Entry ze = new Entry(offset);
|
||||
int versionMadeBy = ZipShort.getValue(this.CFH_BUF, off);
|
||||
off += 2;
|
||||
ze.setPlatform(versionMadeBy >> 8 & 0xF);
|
||||
off += 2;
|
||||
GeneralPurposeBit gpFlag = GeneralPurposeBit.parse(this.CFH_BUF, off);
|
||||
boolean hasUTF8Flag = gpFlag.usesUTF8ForNames();
|
||||
ZipEncoding entryEncoding = hasUTF8Flag ? ZipEncodingHelper.UTF8_ZIP_ENCODING : this.zipEncoding;
|
||||
ze.setGeneralPurposeBit(gpFlag);
|
||||
off += 2;
|
||||
ze.setMethod(ZipShort.getValue(this.CFH_BUF, off));
|
||||
off += 2;
|
||||
long time = ZipUtil.dosToJavaTime(ZipLong.getValue(this.CFH_BUF, off));
|
||||
ze.setTime(time);
|
||||
off += 4;
|
||||
ze.setCrc(ZipLong.getValue(this.CFH_BUF, off));
|
||||
off += 4;
|
||||
ze.setCompressedSize(ZipLong.getValue(this.CFH_BUF, off));
|
||||
off += 4;
|
||||
ze.setSize(ZipLong.getValue(this.CFH_BUF, off));
|
||||
off += 4;
|
||||
int fileNameLen = ZipShort.getValue(this.CFH_BUF, off);
|
||||
off += 2;
|
||||
int extraLen = ZipShort.getValue(this.CFH_BUF, off);
|
||||
off += 2;
|
||||
int commentLen = ZipShort.getValue(this.CFH_BUF, off);
|
||||
off += 2;
|
||||
int diskStart = ZipShort.getValue(this.CFH_BUF, off);
|
||||
off += 2;
|
||||
ze.setInternalAttributes(ZipShort.getValue(this.CFH_BUF, off));
|
||||
off += 2;
|
||||
ze.setExternalAttributes(ZipLong.getValue(this.CFH_BUF, off));
|
||||
off += 4;
|
||||
byte[] fileName = new byte[fileNameLen];
|
||||
this.archive.readFully(fileName);
|
||||
ze.setName(entryEncoding.decode(fileName), fileName);
|
||||
offset.headerOffset = ZipLong.getValue(this.CFH_BUF, off);
|
||||
this.entries.add(ze);
|
||||
byte[] cdExtraData = new byte[extraLen];
|
||||
this.archive.readFully(cdExtraData);
|
||||
ze.setCentralDirectoryExtra(cdExtraData);
|
||||
setSizesAndOffsetFromZip64Extra(ze, offset, diskStart);
|
||||
byte[] comment = new byte[commentLen];
|
||||
this.archive.readFully(comment);
|
||||
ze.setComment(entryEncoding.decode(comment));
|
||||
if (!hasUTF8Flag && this.useUnicodeExtraFields)
|
||||
noUTF8Flag.put(ze, new NameAndComment(fileName, comment));
|
||||
}
|
||||
|
||||
private void setSizesAndOffsetFromZip64Extra(ZipArchiveEntry ze, OffsetEntry offset, int diskStart) throws IOException {
|
||||
Zip64ExtendedInformationExtraField z64 = (Zip64ExtendedInformationExtraField)ze.getExtraField(Zip64ExtendedInformationExtraField.HEADER_ID);
|
||||
if (z64 != null) {
|
||||
boolean hasUncompressedSize = (ze.getSize() == 4294967295L);
|
||||
boolean hasCompressedSize = (ze.getCompressedSize() == 4294967295L);
|
||||
boolean hasRelativeHeaderOffset = (offset.headerOffset == 4294967295L);
|
||||
z64.reparseCentralDirectoryData(hasUncompressedSize, hasCompressedSize, hasRelativeHeaderOffset, (diskStart == 65535));
|
||||
if (hasUncompressedSize) {
|
||||
ze.setSize(z64.getSize().getLongValue());
|
||||
} else if (hasCompressedSize) {
|
||||
z64.setSize(new ZipEightByteInteger(ze.getSize()));
|
||||
}
|
||||
if (hasCompressedSize) {
|
||||
ze.setCompressedSize(z64.getCompressedSize().getLongValue());
|
||||
} else if (hasUncompressedSize) {
|
||||
z64.setCompressedSize(new ZipEightByteInteger(ze.getCompressedSize()));
|
||||
}
|
||||
if (hasRelativeHeaderOffset)
|
||||
offset.headerOffset = z64.getRelativeHeaderOffset().getLongValue();
|
||||
}
|
||||
}
|
||||
|
||||
private void positionAtCentralDirectory() throws IOException {
|
||||
positionAtEndOfCentralDirectoryRecord();
|
||||
boolean found = false;
|
||||
boolean searchedForZip64EOCD = (this.archive.getFilePointer() > 20L);
|
||||
if (searchedForZip64EOCD) {
|
||||
this.archive.seek(this.archive.getFilePointer() - 20L);
|
||||
this.archive.readFully(this.WORD_BUF);
|
||||
found = Arrays.equals(ZipArchiveOutputStream.ZIP64_EOCD_LOC_SIG, this.WORD_BUF);
|
||||
}
|
||||
if (!found) {
|
||||
if (searchedForZip64EOCD)
|
||||
skipBytes(16);
|
||||
positionAtCentralDirectory32();
|
||||
} else {
|
||||
positionAtCentralDirectory64();
|
||||
}
|
||||
}
|
||||
|
||||
private void positionAtCentralDirectory64() throws IOException {
|
||||
skipBytes(4);
|
||||
this.archive.readFully(this.DWORD_BUF);
|
||||
this.archive.seek(ZipEightByteInteger.getLongValue(this.DWORD_BUF));
|
||||
this.archive.readFully(this.WORD_BUF);
|
||||
if (!Arrays.equals(this.WORD_BUF, ZipArchiveOutputStream.ZIP64_EOCD_SIG))
|
||||
throw new ZipException("archive's ZIP64 end of central directory locator is corrupt.");
|
||||
skipBytes(44);
|
||||
this.archive.readFully(this.DWORD_BUF);
|
||||
this.archive.seek(ZipEightByteInteger.getLongValue(this.DWORD_BUF));
|
||||
}
|
||||
|
||||
private void positionAtCentralDirectory32() throws IOException {
|
||||
skipBytes(16);
|
||||
this.archive.readFully(this.WORD_BUF);
|
||||
this.archive.seek(ZipLong.getValue(this.WORD_BUF));
|
||||
}
|
||||
|
||||
private void positionAtEndOfCentralDirectoryRecord() throws IOException {
|
||||
boolean found = tryToLocateSignature(22L, 65557L, ZipArchiveOutputStream.EOCD_SIG);
|
||||
if (!found)
|
||||
throw new ZipException("archive is not a ZIP archive");
|
||||
}
|
||||
|
||||
private boolean tryToLocateSignature(long minDistanceFromEnd, long maxDistanceFromEnd, byte[] sig) throws IOException {
|
||||
boolean found = false;
|
||||
long off = this.archive.length() - minDistanceFromEnd;
|
||||
long stopSearching = Math.max(0L, this.archive.length() - maxDistanceFromEnd);
|
||||
if (off >= 0L)
|
||||
for (; off >= stopSearching; off--) {
|
||||
this.archive.seek(off);
|
||||
int curr = this.archive.read();
|
||||
if (curr == -1)
|
||||
break;
|
||||
if (curr == sig[0]) {
|
||||
curr = this.archive.read();
|
||||
if (curr == sig[1]) {
|
||||
curr = this.archive.read();
|
||||
if (curr == sig[2]) {
|
||||
curr = this.archive.read();
|
||||
if (curr == sig[3]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
this.archive.seek(off);
|
||||
return found;
|
||||
}
|
||||
|
||||
private void skipBytes(int count) throws IOException {
|
||||
int totalSkipped = 0;
|
||||
while (totalSkipped < count) {
|
||||
int skippedNow = this.archive.skipBytes(count - totalSkipped);
|
||||
if (skippedNow <= 0)
|
||||
throw new EOFException();
|
||||
totalSkipped += skippedNow;
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveLocalFileHeaderData(Map<ZipArchiveEntry, NameAndComment> entriesWithoutUTF8Flag) throws IOException {
|
||||
for (ZipArchiveEntry zipArchiveEntry : this.entries) {
|
||||
Entry ze = (Entry)zipArchiveEntry;
|
||||
OffsetEntry offsetEntry = ze.getOffsetEntry();
|
||||
long offset = offsetEntry.headerOffset;
|
||||
this.archive.seek(offset + 26L);
|
||||
this.archive.readFully(this.SHORT_BUF);
|
||||
int fileNameLen = ZipShort.getValue(this.SHORT_BUF);
|
||||
this.archive.readFully(this.SHORT_BUF);
|
||||
int extraFieldLen = ZipShort.getValue(this.SHORT_BUF);
|
||||
int lenToSkip = fileNameLen;
|
||||
while (lenToSkip > 0) {
|
||||
int skipped = this.archive.skipBytes(lenToSkip);
|
||||
if (skipped <= 0)
|
||||
throw new IOException("failed to skip file name in local file header");
|
||||
lenToSkip -= skipped;
|
||||
}
|
||||
byte[] localExtraData = new byte[extraFieldLen];
|
||||
this.archive.readFully(localExtraData);
|
||||
ze.setExtra(localExtraData);
|
||||
offsetEntry.dataOffset = offset + 26L + 2L + 2L + (long)fileNameLen + (long)extraFieldLen;
|
||||
if (entriesWithoutUTF8Flag.containsKey(ze)) {
|
||||
NameAndComment nc = entriesWithoutUTF8Flag.get(ze);
|
||||
ZipUtil.setNameAndCommentFromExtraFields(ze, nc.name, nc.comment);
|
||||
}
|
||||
String name = ze.getName();
|
||||
LinkedList<ZipArchiveEntry> entriesOfThatName = this.nameMap.get(name);
|
||||
if (entriesOfThatName == null) {
|
||||
entriesOfThatName = new LinkedList<ZipArchiveEntry>();
|
||||
this.nameMap.put(name, entriesOfThatName);
|
||||
}
|
||||
entriesOfThatName.addLast(ze);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean startsWithLocalFileHeader() throws IOException {
|
||||
this.archive.seek(0L);
|
||||
this.archive.readFully(this.WORD_BUF);
|
||||
return Arrays.equals(this.WORD_BUF, ZipArchiveOutputStream.LFH_SIG);
|
||||
}
|
||||
|
||||
private class BoundedInputStream extends InputStream {
|
||||
private long remaining;
|
||||
|
||||
private long loc;
|
||||
|
||||
private boolean addDummyByte = false;
|
||||
|
||||
BoundedInputStream(long start, long remaining) {
|
||||
this.remaining = remaining;
|
||||
this.loc = start;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
if (this.remaining-- <= 0L) {
|
||||
if (this.addDummyByte) {
|
||||
this.addDummyByte = false;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
synchronized (ZipFile.this.archive) {
|
||||
ZipFile.this.archive.seek(this.loc++);
|
||||
return ZipFile.this.archive.read();
|
||||
}
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
if (this.remaining <= 0L) {
|
||||
if (this.addDummyByte) {
|
||||
this.addDummyByte = false;
|
||||
b[off] = 0;
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (len <= 0)
|
||||
return 0;
|
||||
if ((long)len > this.remaining)
|
||||
len = (int)this.remaining;
|
||||
int ret = -1;
|
||||
synchronized (ZipFile.this.archive) {
|
||||
ZipFile.this.archive.seek(this.loc);
|
||||
ret = ZipFile.this.archive.read(b, off, len);
|
||||
}
|
||||
if (ret > 0) {
|
||||
this.loc += (long)ret;
|
||||
this.remaining -= (long)ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void addDummy() {
|
||||
this.addDummyByte = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class NameAndComment {
|
||||
private final byte[] name;
|
||||
|
||||
private final byte[] comment;
|
||||
|
||||
private NameAndComment(byte[] name, byte[] comment) {
|
||||
this.name = name;
|
||||
this.comment = comment;
|
||||
}
|
||||
}
|
||||
|
||||
private final Comparator<ZipArchiveEntry> OFFSET_COMPARATOR = new Comparator<ZipArchiveEntry>() {
|
||||
public int compare(ZipArchiveEntry e1, ZipArchiveEntry e2) {
|
||||
if (e1 == e2)
|
||||
return 0;
|
||||
ZipFile.Entry ent1 = (e1 instanceof ZipFile.Entry) ? (ZipFile.Entry)e1 : null;
|
||||
ZipFile.Entry ent2 = (e2 instanceof ZipFile.Entry) ? (ZipFile.Entry)e2 : null;
|
||||
if (ent1 == null)
|
||||
return 1;
|
||||
if (ent2 == null)
|
||||
return -1;
|
||||
long val = (ent1.getOffsetEntry()).headerOffset - (ent2.getOffsetEntry()).headerOffset;
|
||||
return (val == 0L) ? 0 : ((val < 0L) ? -1 : 1);
|
||||
}
|
||||
};
|
||||
|
||||
private static class Entry extends ZipArchiveEntry {
|
||||
private final ZipFile.OffsetEntry offsetEntry;
|
||||
|
||||
Entry(ZipFile.OffsetEntry offset) {
|
||||
this.offsetEntry = offset;
|
||||
}
|
||||
|
||||
ZipFile.OffsetEntry getOffsetEntry() {
|
||||
return this.offsetEntry;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return 3 * super.hashCode() + (int)(this.offsetEntry.headerOffset % Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
public boolean equals(Object other) {
|
||||
if (super.equals(other)) {
|
||||
Entry otherEntry = (Entry)other;
|
||||
return (this.offsetEntry.headerOffset == otherEntry.offsetEntry.headerOffset && this.offsetEntry.dataOffset == otherEntry.offsetEntry.dataOffset);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public final class ZipLong implements Cloneable, Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final int BYTE_1 = 1;
|
||||
|
||||
private static final int BYTE_1_MASK = 65280;
|
||||
|
||||
private static final int BYTE_1_SHIFT = 8;
|
||||
|
||||
private static final int BYTE_2 = 2;
|
||||
|
||||
private static final int BYTE_2_MASK = 16711680;
|
||||
|
||||
private static final int BYTE_2_SHIFT = 16;
|
||||
|
||||
private static final int BYTE_3 = 3;
|
||||
|
||||
private static final long BYTE_3_MASK = 4278190080L;
|
||||
|
||||
private static final int BYTE_3_SHIFT = 24;
|
||||
|
||||
private final long value;
|
||||
|
||||
public static final ZipLong CFH_SIG = new ZipLong(33639248L);
|
||||
|
||||
public static final ZipLong LFH_SIG = new ZipLong(67324752L);
|
||||
|
||||
public static final ZipLong DD_SIG = new ZipLong(134695760L);
|
||||
|
||||
static final ZipLong ZIP64_MAGIC = new ZipLong(4294967295L);
|
||||
|
||||
public static final ZipLong SINGLE_SEGMENT_SPLIT_MARKER = new ZipLong(808471376L);
|
||||
|
||||
public static final ZipLong AED_SIG = new ZipLong(134630224L);
|
||||
|
||||
public ZipLong(long value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public ZipLong(byte[] bytes) {
|
||||
this(bytes, 0);
|
||||
}
|
||||
|
||||
public ZipLong(byte[] bytes, int offset) {
|
||||
this.value = getValue(bytes, offset);
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return getBytes(this.value);
|
||||
}
|
||||
|
||||
public long getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public static byte[] getBytes(long value) {
|
||||
byte[] result = new byte[4];
|
||||
result[0] = (byte)(int)(value & 0xFFL);
|
||||
result[1] = (byte)(int)((value & 0xFF00L) >> 8L);
|
||||
result[2] = (byte)(int)((value & 0xFF0000L) >> 16L);
|
||||
result[3] = (byte)(int)((value & 0xFF000000L) >> 24L);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static long getValue(byte[] bytes, int offset) {
|
||||
long value = (long)(bytes[offset + 3] << 24) & 0xFF000000L;
|
||||
value += (long)(bytes[offset + 2] << 16 & 0xFF0000);
|
||||
value += (long)(bytes[offset + 1] << 8 & 0xFF00);
|
||||
value += (long)(bytes[offset] & 0xFF);
|
||||
return value;
|
||||
}
|
||||
|
||||
public static long getValue(byte[] bytes) {
|
||||
return getValue(bytes, 0);
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || !(o instanceof ZipLong))
|
||||
return false;
|
||||
return (this.value == ((ZipLong)o).getValue());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return (int)this.value;
|
||||
}
|
||||
|
||||
public Object clone() {
|
||||
try {
|
||||
return super.clone();
|
||||
} catch (CloneNotSupportedException cnfe) {
|
||||
throw new RuntimeException(cnfe);
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "ZipLong value: " + this.value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public enum ZipMethod {
|
||||
STORED(0),
|
||||
UNSHRINKING(1),
|
||||
EXPANDING_LEVEL_1(2),
|
||||
EXPANDING_LEVEL_2(3),
|
||||
EXPANDING_LEVEL_3(4),
|
||||
EXPANDING_LEVEL_4(5),
|
||||
IMPLODING(6),
|
||||
TOKENIZATION(7),
|
||||
DEFLATED(8),
|
||||
ENHANCED_DEFLATED(9),
|
||||
PKWARE_IMPLODING(10),
|
||||
BZIP2(12),
|
||||
LZMA(14),
|
||||
JPEG(96),
|
||||
WAVPACK(97),
|
||||
PPMD(98),
|
||||
AES_ENCRYPTED(99),
|
||||
UNKNOWN(-1);
|
||||
|
||||
private final int code;
|
||||
|
||||
private static final Map<Integer, ZipMethod> codeToEnum;
|
||||
|
||||
static {
|
||||
Map<Integer, ZipMethod> cte = new HashMap<Integer, ZipMethod>();
|
||||
for (ZipMethod method : values())
|
||||
cte.put(Integer.valueOf(method.getCode()), method);
|
||||
codeToEnum = Collections.<Integer, ZipMethod>unmodifiableMap(cte);
|
||||
}
|
||||
|
||||
ZipMethod(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public static ZipMethod getMethodByCode(int code) {
|
||||
return codeToEnum.get(Integer.valueOf(code));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
package org.apache.commons.compress.archivers.zip;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public final class ZipShort implements Cloneable, Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final int BYTE_1_MASK = 65280;
|
||||
|
||||
private static final int BYTE_1_SHIFT = 8;
|
||||
|
||||
private final int value;
|
||||
|
||||
public ZipShort(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public ZipShort(byte[] bytes) {
|
||||
this(bytes, 0);
|
||||
}
|
||||
|
||||
public ZipShort(byte[] bytes, int offset) {
|
||||
this.value = getValue(bytes, offset);
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
byte[] result = new byte[2];
|
||||
result[0] = (byte)(this.value & 0xFF);
|
||||
result[1] = (byte)((this.value & 0xFF00) >> 8);
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public static byte[] getBytes(int value) {
|
||||
byte[] result = new byte[2];
|
||||
result[0] = (byte)(value & 0xFF);
|
||||
result[1] = (byte)((value & 0xFF00) >> 8);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int getValue(byte[] bytes, int offset) {
|
||||
int value = bytes[offset + 1] << 8 & 0xFF00;
|
||||
value += bytes[offset] & 0xFF;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static int getValue(byte[] bytes) {
|
||||
return getValue(bytes, 0);
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || !(o instanceof ZipShort))
|
||||
return false;
|
||||
return (this.value == ((ZipShort)o).getValue());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public Object clone() {
|
||||
try {
|
||||
return super.clone();
|
||||
} catch (CloneNotSupportedException cnfe) {
|
||||
throw new RuntimeException(cnfe);
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "ZipShort value: " + this.value;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue