You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

Tar.java 37 kB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
8 years ago
8 years ago
11 years ago
11 years ago
8 years ago
11 years ago
8 years ago
8 years ago
8 years ago
11 years ago
8 years ago
11 years ago
8 years ago
8 years ago
11 years ago
11 years ago
7 years ago
11 years ago
8 years ago
11 years ago
8 years ago
8 years ago
11 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
11 years ago
8 years ago
11 years ago
8 years ago
8 years ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * https://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. package org.apache.tools.ant.taskdefs;
  19. import java.io.BufferedOutputStream;
  20. import java.io.File;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.io.OutputStream;
  24. import java.lang.reflect.Constructor;
  25. import java.lang.reflect.InvocationTargetException;
  26. import java.nio.file.Files;
  27. import java.util.Collection;
  28. import java.util.HashMap;
  29. import java.util.HashSet;
  30. import java.util.List;
  31. import java.util.Map;
  32. import java.util.Set;
  33. import java.util.Vector;
  34. import java.util.zip.GZIPOutputStream;
  35. import org.apache.tools.ant.BuildException;
  36. import org.apache.tools.ant.DirectoryScanner;
  37. import org.apache.tools.ant.Project;
  38. import org.apache.tools.ant.types.ArchiveFileSet;
  39. import org.apache.tools.ant.types.EnumeratedAttribute;
  40. import org.apache.tools.ant.types.FileSet;
  41. import org.apache.tools.ant.types.Resource;
  42. import org.apache.tools.ant.types.ResourceCollection;
  43. import org.apache.tools.ant.types.resources.ArchiveResource;
  44. import org.apache.tools.ant.types.resources.FileProvider;
  45. import org.apache.tools.ant.types.resources.FileResource;
  46. import org.apache.tools.ant.types.resources.TarResource;
  47. import org.apache.tools.ant.types.selectors.SelectorUtils;
  48. import org.apache.tools.ant.util.FileUtils;
  49. import org.apache.tools.ant.util.MergingMapper;
  50. import org.apache.tools.ant.util.ResourceUtils;
  51. import org.apache.tools.ant.util.SourceFileScanner;
  52. import org.apache.tools.bzip2.CBZip2OutputStream;
  53. import org.apache.tools.tar.TarConstants;
  54. import org.apache.tools.tar.TarEntry;
  55. import org.apache.tools.tar.TarOutputStream;
  56. /**
  57. * Creates a tar archive.
  58. *
  59. * @since Ant 1.1
  60. *
  61. * @ant.task category="packaging"
  62. */
  63. public class Tar extends MatchingTask {
  64. private static final int BUFFER_SIZE = 8 * 1024;
  65. /**
  66. * @deprecated since 1.5.x.
  67. * Tar.WARN is deprecated and is replaced with
  68. * Tar.TarLongFileMode.WARN
  69. */
  70. @Deprecated
  71. public static final String WARN = "warn";
  72. /**
  73. * @deprecated since 1.5.x.
  74. * Tar.FAIL is deprecated and is replaced with
  75. * Tar.TarLongFileMode.FAIL
  76. */
  77. @Deprecated
  78. public static final String FAIL = "fail";
  79. /**
  80. * @deprecated since 1.5.x.
  81. * Tar.TRUNCATE is deprecated and is replaced with
  82. * Tar.TarLongFileMode.TRUNCATE
  83. */
  84. @Deprecated
  85. public static final String TRUNCATE = "truncate";
  86. /**
  87. * @deprecated since 1.5.x.
  88. * Tar.GNU is deprecated and is replaced with
  89. * Tar.TarLongFileMode.GNU
  90. */
  91. @Deprecated
  92. public static final String GNU = "gnu";
  93. /**
  94. * @deprecated since 1.5.x.
  95. * Tar.OMIT is deprecated and is replaced with
  96. * Tar.TarLongFileMode.OMIT
  97. */
  98. @Deprecated
  99. public static final String OMIT = "omit";
  100. // CheckStyle:VisibilityModifier OFF - bc
  101. File tarFile;
  102. File baseDir;
  103. private TarLongFileMode longFileMode = new TarLongFileMode();
  104. // need to keep the package private version for backwards compatibility
  105. Vector<TarFileSet> filesets = new Vector<>();
  106. // we must keep two lists since other classes may modify the
  107. // filesets Vector (it is package private) without us noticing
  108. private final List<ResourceCollection> resourceCollections = new Vector<>();
  109. // CheckStyle:VisibilityModifier ON
  110. /**
  111. * Indicates whether the user has been warned about long files already.
  112. */
  113. private boolean longWarningGiven = false;
  114. private TarCompressionMethod compression = new TarCompressionMethod();
  115. /**
  116. * Encoding to use for filenames, defaults to the platform's
  117. * default encoding.
  118. */
  119. private String encoding;
  120. /**
  121. * Add a new fileset with the option to specify permissions
  122. * @return the tar fileset to be used as the nested element.
  123. */
  124. public TarFileSet createTarFileSet() {
  125. final TarFileSet fs = new TarFileSet();
  126. fs.setProject(getProject());
  127. filesets.addElement(fs);
  128. return fs;
  129. }
  130. /**
  131. * Add a collection of resources to archive.
  132. * @param res a resource collection to archive.
  133. * @since Ant 1.7
  134. */
  135. public void add(final ResourceCollection res) {
  136. resourceCollections.add(res);
  137. }
  138. /**
  139. * Set is the name/location of where to create the tar file.
  140. * @param tarFile the location of the tar file.
  141. * @deprecated since 1.5.x.
  142. * For consistency with other tasks, please use setDestFile().
  143. */
  144. @Deprecated
  145. public void setTarfile(final File tarFile) {
  146. this.tarFile = tarFile;
  147. }
  148. /**
  149. * Set is the name/location of where to create the tar file.
  150. * @since Ant 1.5
  151. * @param destFile The output of the tar
  152. */
  153. public void setDestFile(final File destFile) {
  154. this.tarFile = destFile;
  155. }
  156. /**
  157. * This is the base directory to look in for things to tar.
  158. * @param baseDir the base directory.
  159. */
  160. public void setBasedir(final File baseDir) {
  161. this.baseDir = baseDir;
  162. }
  163. /**
  164. * Set how to handle long files, those with a path&gt;100 chars.
  165. * Optional, default=warn.
  166. * <p>
  167. * Allowable values are
  168. * <ul>
  169. * <li> truncate - paths are truncated to the maximum length
  170. * <li> fail - paths greater than the maximum cause a build exception
  171. * <li> warn - paths greater than the maximum cause a warning and GNU is used
  172. * <li> gnu - GNU extensions are used for any paths greater than the maximum.
  173. * <li> omit - paths greater than the maximum are omitted from the archive
  174. * </ul>
  175. * @param mode the mode string to handle long files.
  176. * @deprecated since 1.5.x.
  177. * setLongFile(String) is deprecated and is replaced with
  178. * setLongFile(Tar.TarLongFileMode) to make Ant's Introspection
  179. * mechanism do the work and also to encapsulate operations on
  180. * the mode in its own class.
  181. */
  182. @Deprecated
  183. public void setLongfile(final String mode) {
  184. log("DEPRECATED - The setLongfile(String) method has been deprecated. Use setLongfile(Tar.TarLongFileMode) instead.");
  185. this.longFileMode = new TarLongFileMode();
  186. longFileMode.setValue(mode);
  187. }
  188. /**
  189. * Set how to handle long files, those with a path&gt;100 chars.
  190. * Optional, default=warn.
  191. * <p>
  192. * Allowable values are
  193. * <ul>
  194. * <li> truncate - paths are truncated to the maximum length
  195. * <li> fail - paths greater than the maximum cause a build exception
  196. * <li> warn - paths greater than the maximum cause a warning and GNU is used
  197. * <li> gnu - extensions used by older versions of GNU tar are used for any paths greater than the maximum.
  198. * <li> posix - use POSIX PAX extension headers for any paths greater than the maximum. Supported by all modern tar implementations.
  199. * <li> omit - paths greater than the maximum are omitted from the archive
  200. * </ul>
  201. * @param mode the mode to handle long file names.
  202. */
  203. public void setLongfile(final TarLongFileMode mode) {
  204. this.longFileMode = mode;
  205. }
  206. /**
  207. * Set compression method.
  208. * Allowable values are
  209. * <ul>
  210. * <li> none - no compression
  211. * <li> gzip - Gzip compression
  212. * <li> bzip2 - Bzip2 compression
  213. * <li>xz - XZ compression, requires XZ for Java
  214. * </ul>
  215. * @param mode the compression method.
  216. */
  217. public void setCompression(final TarCompressionMethod mode) {
  218. this.compression = mode;
  219. }
  220. /**
  221. * Encoding to use for filenames, defaults to the platform's
  222. * default encoding.
  223. *
  224. * <p>For a list of possible values see <a
  225. * href="https://docs.oracle.com/javase/8/docs/technotes/guides/intl/encoding.doc.html">https://docs.oracle.com/javase/8/docs/technotes/guides/intl/encoding.doc.html</a>.</p>
  226. * @param encoding the encoding name
  227. *
  228. * @since Ant 1.9.5
  229. */
  230. public void setEncoding(final String encoding) {
  231. this.encoding = encoding;
  232. }
  233. /**
  234. * do the business
  235. * @throws BuildException on error
  236. */
  237. @Override
  238. public void execute() throws BuildException {
  239. if (tarFile == null) {
  240. throw new BuildException("tarfile attribute must be set!",
  241. getLocation());
  242. }
  243. if (tarFile.exists() && tarFile.isDirectory()) {
  244. throw new BuildException("tarfile is a directory!",
  245. getLocation());
  246. }
  247. if (tarFile.exists() && !tarFile.canWrite()) {
  248. throw new BuildException("Can not write to the specified tarfile!",
  249. getLocation());
  250. }
  251. final Vector<TarFileSet> savedFileSets = new Vector<>(filesets);
  252. try {
  253. if (baseDir != null) {
  254. if (!baseDir.exists()) {
  255. throw new BuildException("basedir does not exist!",
  256. getLocation());
  257. }
  258. // add the main fileset to the list of filesets to process.
  259. final TarFileSet mainFileSet = new TarFileSet(fileset);
  260. mainFileSet.setDir(baseDir);
  261. filesets.addElement(mainFileSet);
  262. }
  263. if (filesets.isEmpty() && resourceCollections.isEmpty()) {
  264. throw new BuildException(
  265. "You must supply either a basedir attribute or some nested resource collections.",
  266. getLocation());
  267. }
  268. // check if tar is out of date with respect to each
  269. // fileset
  270. boolean upToDate = true;
  271. for (final TarFileSet tfs : filesets) {
  272. upToDate &= check(tfs);
  273. }
  274. for (final ResourceCollection rcol : resourceCollections) {
  275. upToDate &= check(rcol);
  276. }
  277. if (upToDate) {
  278. log("Nothing to do: " + tarFile.getAbsolutePath()
  279. + " is up to date.", Project.MSG_INFO);
  280. return;
  281. }
  282. final File parent = tarFile.getParentFile();
  283. if (parent != null && !parent.isDirectory()
  284. && !(parent.mkdirs() || parent.isDirectory())) {
  285. throw new BuildException(
  286. "Failed to create missing parent directory for %s",
  287. tarFile);
  288. }
  289. log("Building tar: " + tarFile.getAbsolutePath(), Project.MSG_INFO);
  290. try (TarOutputStream tOut = new TarOutputStream(
  291. compression.compress(new BufferedOutputStream(
  292. Files.newOutputStream(tarFile.toPath()))),
  293. encoding)) {
  294. tOut.setDebug(true);
  295. if (longFileMode.isTruncateMode()) {
  296. tOut.setLongFileMode(TarOutputStream.LONGFILE_TRUNCATE);
  297. } else if (longFileMode.isFailMode()
  298. || longFileMode.isOmitMode()) {
  299. tOut.setLongFileMode(TarOutputStream.LONGFILE_ERROR);
  300. } else if (longFileMode.isPosixMode()) {
  301. tOut.setLongFileMode(TarOutputStream.LONGFILE_POSIX);
  302. } else {
  303. // warn or GNU
  304. tOut.setLongFileMode(TarOutputStream.LONGFILE_GNU);
  305. }
  306. longWarningGiven = false;
  307. for (final TarFileSet tfs : filesets) {
  308. tar(tfs, tOut);
  309. }
  310. for (final ResourceCollection rcol : resourceCollections) {
  311. tar(rcol, tOut);
  312. }
  313. } catch (final IOException ioe) {
  314. final String msg = "Problem creating TAR: " + ioe.getMessage();
  315. throw new BuildException(msg, ioe, getLocation());
  316. }
  317. } finally {
  318. filesets = savedFileSets;
  319. }
  320. }
  321. /**
  322. * tar a file
  323. * @param file the file to tar
  324. * @param tOut the output stream
  325. * @param vPath the path name of the file to tar
  326. * @param tarFileSet the fileset that the file came from.
  327. * @throws IOException on error
  328. */
  329. protected void tarFile(final File file, final TarOutputStream tOut, final String vPath,
  330. final TarFileSet tarFileSet)
  331. throws IOException {
  332. if (file.equals(tarFile)) {
  333. // If the archive is built for the first time and it is
  334. // matched by a resource collection, then it hasn't been
  335. // found in check (it hasn't been there) but will be
  336. // included now.
  337. //
  338. // for some strange reason the old code would simply skip
  339. // the entry and not fail, do the same now for backwards
  340. // compatibility reasons. Without this, the which4j build
  341. // fails in Gump
  342. return;
  343. }
  344. tarResource(new FileResource(file), tOut, vPath, tarFileSet);
  345. }
  346. /**
  347. * tar a resource
  348. * @param r the resource to tar
  349. * @param tOut the output stream
  350. * @param vPath the path name of the file to tar
  351. * @param tarFileSet the fileset that the file came from, may be null.
  352. * @throws IOException on error
  353. * @since Ant 1.7
  354. */
  355. protected void tarResource(final Resource r, final TarOutputStream tOut, String vPath,
  356. final TarFileSet tarFileSet)
  357. throws IOException {
  358. if (!r.isExists()) {
  359. return;
  360. }
  361. boolean preserveLeadingSlashes = false;
  362. if (tarFileSet != null) {
  363. final String fullpath = tarFileSet.getFullpath(this.getProject());
  364. if (fullpath.isEmpty()) {
  365. // don't add "" to the archive
  366. if (vPath.isEmpty()) {
  367. return;
  368. }
  369. String prefix = tarFileSet.getPrefix(this.getProject());
  370. // '/' is appended for compatibility with the zip task.
  371. if (!prefix.isEmpty() && !prefix.endsWith("/")) {
  372. prefix += "/";
  373. }
  374. vPath = prefix + vPath;
  375. } else {
  376. vPath = fullpath;
  377. }
  378. preserveLeadingSlashes = tarFileSet.getPreserveLeadingSlashes();
  379. if (vPath.startsWith("/") && !preserveLeadingSlashes) {
  380. final int l = vPath.length();
  381. if (l <= 1) {
  382. // we would end up adding "" to the archive
  383. return;
  384. }
  385. vPath = vPath.substring(1, l);
  386. }
  387. }
  388. if (r.isDirectory() && !vPath.endsWith("/")) {
  389. vPath += "/";
  390. }
  391. if (vPath.length() >= TarConstants.NAMELEN) {
  392. if (longFileMode.isOmitMode()) {
  393. log("Omitting: " + vPath, Project.MSG_INFO);
  394. return;
  395. } else if (longFileMode.isWarnMode()) {
  396. log("Entry: " + vPath + " longer than "
  397. + TarConstants.NAMELEN + " characters.",
  398. Project.MSG_WARN);
  399. if (!longWarningGiven) {
  400. log("Resulting tar file can only be processed "
  401. + "successfully by GNU compatible tar commands",
  402. Project.MSG_WARN);
  403. longWarningGiven = true;
  404. }
  405. } else if (longFileMode.isFailMode()) {
  406. throw new BuildException("Entry: " + vPath
  407. + " longer than " + TarConstants.NAMELEN
  408. + "characters.", getLocation());
  409. }
  410. }
  411. final TarEntry te = new TarEntry(vPath, preserveLeadingSlashes);
  412. te.setModTime(r.getLastModified());
  413. // preserve permissions
  414. if (r instanceof ArchiveResource) {
  415. final ArchiveResource ar = (ArchiveResource) r;
  416. te.setMode(ar.getMode());
  417. if (r instanceof TarResource) {
  418. final TarResource tr = (TarResource) r;
  419. te.setUserName(tr.getUserName());
  420. te.setUserId(tr.getLongUid());
  421. te.setGroupName(tr.getGroup());
  422. te.setGroupId(tr.getLongGid());
  423. }
  424. }
  425. if (!r.isDirectory()) {
  426. if (r.size() > TarConstants.MAXSIZE) {
  427. throw new BuildException(
  428. "Resource: " + r + " larger than "
  429. + TarConstants.MAXSIZE + " bytes.");
  430. }
  431. te.setSize(r.getSize());
  432. // override permissions if set explicitly
  433. if (tarFileSet != null && tarFileSet.hasFileModeBeenSet()) {
  434. te.setMode(tarFileSet.getMode());
  435. }
  436. } else if (tarFileSet != null && tarFileSet.hasDirModeBeenSet()) {
  437. // override permissions if set explicitly
  438. te.setMode(tarFileSet.getDirMode(this.getProject()));
  439. }
  440. if (tarFileSet != null) {
  441. // only override permissions if set explicitly
  442. if (tarFileSet.hasUserNameBeenSet()) {
  443. te.setUserName(tarFileSet.getUserName());
  444. }
  445. if (tarFileSet.hasGroupBeenSet()) {
  446. te.setGroupName(tarFileSet.getGroup());
  447. }
  448. if (tarFileSet.hasUserIdBeenSet()) {
  449. te.setUserId(tarFileSet.getUid());
  450. }
  451. if (tarFileSet.hasGroupIdBeenSet()) {
  452. te.setGroupId(tarFileSet.getGid());
  453. }
  454. }
  455. InputStream in = null;
  456. try {
  457. tOut.putNextEntry(te);
  458. if (!r.isDirectory()) {
  459. in = r.getInputStream();
  460. final byte[] buffer = new byte[BUFFER_SIZE];
  461. int count = 0;
  462. do {
  463. tOut.write(buffer, 0, count);
  464. count = in.read(buffer, 0, buffer.length);
  465. } while (count != -1);
  466. }
  467. tOut.closeEntry();
  468. } finally {
  469. FileUtils.close(in);
  470. }
  471. }
  472. /**
  473. * Is the archive up to date in relationship to a list of files.
  474. * @param files the files to check
  475. * @return true if the archive is up to date.
  476. * @deprecated since 1.5.x.
  477. * use the two-arg version instead.
  478. */
  479. @Deprecated
  480. protected boolean archiveIsUpToDate(final String[] files) {
  481. return archiveIsUpToDate(files, baseDir);
  482. }
  483. /**
  484. * Is the archive up to date in relationship to a list of files.
  485. * @param files the files to check
  486. * @param dir the base directory for the files.
  487. * @return true if the archive is up to date.
  488. * @since Ant 1.5.2
  489. */
  490. protected boolean archiveIsUpToDate(final String[] files, final File dir) {
  491. final SourceFileScanner sfs = new SourceFileScanner(this);
  492. final MergingMapper mm = new MergingMapper();
  493. mm.setTo(tarFile.getAbsolutePath());
  494. return sfs.restrict(files, dir, null, mm).length == 0;
  495. }
  496. /**
  497. * Is the archive up to date in relationship to a list of files.
  498. * @param r the files to check
  499. * @return true if the archive is up to date.
  500. * @since Ant 1.7
  501. */
  502. protected boolean archiveIsUpToDate(final Resource r) {
  503. return SelectorUtils.isOutOfDate(new FileResource(tarFile), r,
  504. FileUtils.getFileUtils()
  505. .getFileTimestampGranularity());
  506. }
  507. /**
  508. * Whether this task can deal with non-file resources.
  509. *
  510. * <p>This implementation returns true only if this task is
  511. * &lt;tar&gt;. Any subclass of this class that also wants to
  512. * support non-file resources needs to override this method. We
  513. * need to do so for backwards compatibility reasons since we
  514. * can't expect subclasses to support resources.</p>
  515. * @return true for this task.
  516. * @since Ant 1.7
  517. */
  518. protected boolean supportsNonFileResources() {
  519. return getClass().equals(Tar.class);
  520. }
  521. /**
  522. * Checks whether the archive is out-of-date with respect to the resources
  523. * of the given collection.
  524. *
  525. * <p>Also checks that either all collections only contain file
  526. * resources or this class supports non-file collections.</p>
  527. *
  528. * <p>And - in case of file-collections - ensures that the archive won't
  529. * contain itself.</p>
  530. *
  531. * @param rc the resource collection to check
  532. * @return whether the archive is up-to-date
  533. * @since Ant 1.7
  534. */
  535. protected boolean check(final ResourceCollection rc) {
  536. boolean upToDate = true;
  537. if (isFileFileSet(rc)) {
  538. final FileSet fs = (FileSet) rc;
  539. upToDate = check(fs.getDir(getProject()), getFileNames(fs));
  540. } else if (!rc.isFilesystemOnly() && !supportsNonFileResources()) {
  541. throw new BuildException("only filesystem resources are supported");
  542. } else if (rc.isFilesystemOnly()) {
  543. final Set<File> basedirs = new HashSet<>();
  544. final Map<File, List<String>> basedirToFilesMap = new HashMap<>();
  545. for (final Resource res : rc) {
  546. final FileResource r = ResourceUtils
  547. .asFileResource(res.as(FileProvider.class));
  548. File base = r.getBaseDir();
  549. if (base == null) {
  550. base = Copy.NULL_FILE_PLACEHOLDER;
  551. }
  552. basedirs.add(base);
  553. List<String> files = basedirToFilesMap.computeIfAbsent(base, k -> new Vector<>());
  554. if (base == Copy.NULL_FILE_PLACEHOLDER) {
  555. files.add(r.getFile().getAbsolutePath());
  556. } else {
  557. files.add(r.getName());
  558. }
  559. }
  560. for (final File base : basedirs) {
  561. final File tmpBase = base == Copy.NULL_FILE_PLACEHOLDER ? null : base;
  562. final List<String> files = basedirToFilesMap.get(base);
  563. upToDate &= check(tmpBase, files);
  564. }
  565. } else { // non-file resources
  566. for (Resource r : rc) {
  567. upToDate = archiveIsUpToDate(r);
  568. }
  569. }
  570. return upToDate;
  571. }
  572. /**
  573. * <p>Checks whether the archive is out-of-date with respect to the
  574. * given files, ensures that the archive won't contain itself.</p>
  575. *
  576. * @param basedir base directory for file names
  577. * @param files array of relative file names
  578. * @return whether the archive is up-to-date
  579. * @since Ant 1.7
  580. */
  581. protected boolean check(final File basedir, final String[] files) {
  582. boolean upToDate = true;
  583. if (!archiveIsUpToDate(files, basedir)) {
  584. upToDate = false;
  585. }
  586. for (String file : files) {
  587. if (tarFile.equals(new File(basedir, file))) {
  588. throw new BuildException("A tar file cannot include itself",
  589. getLocation());
  590. }
  591. }
  592. return upToDate;
  593. }
  594. /**
  595. * <p>Checks whether the archive is out-of-date with respect to the
  596. * given files, ensures that the archive won't contain itself.</p>
  597. *
  598. * @param basedir base directory for file names
  599. * @param files array of relative file names
  600. * @return whether the archive is up-to-date
  601. * @see #check(File, String[])
  602. * @since Ant 1.9.5
  603. */
  604. protected boolean check(final File basedir, final Collection<String> files) {
  605. return check(basedir, files.toArray(new String[files.size()]));
  606. }
  607. /**
  608. * Adds the resources contained in this collection to the archive.
  609. *
  610. * <p>Uses the file based methods for file resources for backwards
  611. * compatibility.</p>
  612. *
  613. * @param rc the collection containing resources to add
  614. * @param tOut stream writing to the archive.
  615. * @throws IOException on error.
  616. * @since Ant 1.7
  617. */
  618. protected void tar(final ResourceCollection rc, final TarOutputStream tOut)
  619. throws IOException {
  620. ArchiveFileSet afs = null;
  621. if (rc instanceof ArchiveFileSet) {
  622. afs = (ArchiveFileSet) rc;
  623. }
  624. if (afs != null && afs.size() > 1
  625. && !afs.getFullpath(this.getProject()).isEmpty()) {
  626. throw new BuildException(
  627. "fullpath attribute may only be specified for filesets that specify a single file.");
  628. }
  629. final TarFileSet tfs = asTarFileSet(afs);
  630. if (isFileFileSet(rc)) {
  631. final FileSet fs = (FileSet) rc;
  632. for (String file : getFileNames(fs)) {
  633. final File f = new File(fs.getDir(getProject()), file);
  634. final String name = file.replace(File.separatorChar, '/');
  635. tarFile(f, tOut, name, tfs);
  636. }
  637. } else if (rc.isFilesystemOnly()) {
  638. for (final Resource r : rc) {
  639. final File f = r.as(FileProvider.class).getFile();
  640. tarFile(f, tOut, f.getName(), tfs);
  641. }
  642. } else { // non-file resources
  643. for (final Resource r : rc) {
  644. tarResource(r, tOut, r.getName(), tfs);
  645. }
  646. }
  647. }
  648. /**
  649. * whether the given resource collection is a (subclass of)
  650. * FileSet that only contains file system resources.
  651. * @param rc the resource collection to check.
  652. * @return true if the collection is a fileset.
  653. * @since Ant 1.7
  654. */
  655. protected static boolean isFileFileSet(final ResourceCollection rc) {
  656. return rc instanceof FileSet && rc.isFilesystemOnly();
  657. }
  658. /**
  659. * Grabs all included files and directors from the FileSet and
  660. * returns them as an array of (relative) file names.
  661. * @param fs the fileset to operate on.
  662. * @return a list of the filenames.
  663. * @since Ant 1.7
  664. */
  665. protected static String[] getFileNames(final FileSet fs) {
  666. final DirectoryScanner ds = fs.getDirectoryScanner(fs.getProject());
  667. final String[] directories = ds.getIncludedDirectories();
  668. final String[] filesPerSe = ds.getIncludedFiles();
  669. final String[] files = new String[directories.length + filesPerSe.length];
  670. System.arraycopy(directories, 0, files, 0, directories.length);
  671. System.arraycopy(filesPerSe, 0, files, directories.length,
  672. filesPerSe.length);
  673. return files;
  674. }
  675. /**
  676. * Copies fullpath, prefix and permission attributes from the
  677. * ArchiveFileSet to a new TarFileSet (or returns it unchanged if
  678. * it already is a TarFileSet).
  679. *
  680. * @param archiveFileSet fileset to copy attributes from, may be null
  681. * @return a new TarFileSet.
  682. * @since Ant 1.7
  683. */
  684. protected TarFileSet asTarFileSet(final ArchiveFileSet archiveFileSet) {
  685. TarFileSet tfs;
  686. if (archiveFileSet instanceof TarFileSet) {
  687. tfs = (TarFileSet) archiveFileSet;
  688. } else {
  689. tfs = new TarFileSet();
  690. tfs.setProject(getProject());
  691. if (archiveFileSet != null) {
  692. tfs.setPrefix(archiveFileSet.getPrefix(getProject()));
  693. tfs.setFullpath(archiveFileSet.getFullpath(getProject()));
  694. if (archiveFileSet.hasFileModeBeenSet()) {
  695. tfs.integerSetFileMode(archiveFileSet
  696. .getFileMode(getProject()));
  697. }
  698. if (archiveFileSet.hasDirModeBeenSet()) {
  699. tfs.integerSetDirMode(archiveFileSet
  700. .getDirMode(getProject()));
  701. }
  702. if (archiveFileSet
  703. instanceof org.apache.tools.ant.types.TarFileSet) {
  704. final org.apache.tools.ant.types.TarFileSet t =
  705. (org.apache.tools.ant.types.TarFileSet) archiveFileSet;
  706. if (t.hasUserNameBeenSet()) {
  707. tfs.setUserName(t.getUserName());
  708. }
  709. if (t.hasGroupBeenSet()) {
  710. tfs.setGroup(t.getGroup());
  711. }
  712. if (t.hasUserIdBeenSet()) {
  713. tfs.setUid(t.getUid());
  714. }
  715. if (t.hasGroupIdBeenSet()) {
  716. tfs.setGid(t.getGid());
  717. }
  718. }
  719. }
  720. }
  721. return tfs;
  722. }
  723. /**
  724. * This is a FileSet with the option to specify permissions
  725. * and other attributes.
  726. */
  727. public static class TarFileSet
  728. extends org.apache.tools.ant.types.TarFileSet {
  729. private String[] files = null;
  730. private boolean preserveLeadingSlashes = false;
  731. /**
  732. * Creates a new <code>TarFileSet</code> instance.
  733. * Using a fileset as a constructor argument.
  734. *
  735. * @param fileset a <code>FileSet</code> value
  736. */
  737. public TarFileSet(final FileSet fileset) {
  738. super(fileset);
  739. }
  740. /**
  741. * Creates a new <code>TarFileSet</code> instance.
  742. *
  743. */
  744. public TarFileSet() {
  745. super();
  746. }
  747. /**
  748. * Get a list of files and directories specified in the fileset.
  749. * @param p the current project.
  750. * @return a list of file and directory names, relative to
  751. * the baseDir for the project.
  752. */
  753. public String[] getFiles(final Project p) {
  754. if (files == null) {
  755. files = getFileNames(this);
  756. }
  757. return files;
  758. }
  759. /**
  760. * A 3 digit octal string, specify the user, group and
  761. * other modes in the standard Unix fashion;
  762. * optional, default=0644
  763. * @param octalString a 3 digit octal string.
  764. */
  765. public void setMode(final String octalString) {
  766. setFileMode(octalString);
  767. }
  768. /**
  769. * @return the current mode.
  770. */
  771. public int getMode() {
  772. return getFileMode(this.getProject());
  773. }
  774. /**
  775. * Flag to indicates whether leading `/'s should
  776. * be preserved in the file names.
  777. * Optional, default is <code>false</code>.
  778. * @param b the leading slashes flag.
  779. */
  780. public void setPreserveLeadingSlashes(final boolean b) {
  781. this.preserveLeadingSlashes = b;
  782. }
  783. /**
  784. * @return the leading slashes flag.
  785. */
  786. public boolean getPreserveLeadingSlashes() {
  787. return preserveLeadingSlashes;
  788. }
  789. }
  790. /**
  791. * Set of options for long file handling in the task.
  792. *
  793. */
  794. public static class TarLongFileMode extends EnumeratedAttribute {
  795. /** permissible values for longfile attribute */
  796. public static final String
  797. WARN = "warn",
  798. FAIL = "fail",
  799. TRUNCATE = "truncate",
  800. GNU = "gnu",
  801. POSIX = "posix",
  802. OMIT = "omit";
  803. private static final String[] VALID_MODES = {
  804. WARN, FAIL, TRUNCATE, GNU, POSIX, OMIT
  805. };
  806. /** Constructor, defaults to "warn" */
  807. public TarLongFileMode() {
  808. super();
  809. setValue(WARN);
  810. }
  811. /**
  812. * @return the possible values for this enumerated type.
  813. */
  814. @Override
  815. public String[] getValues() {
  816. return VALID_MODES;
  817. }
  818. /**
  819. * @return true if value is "truncate".
  820. */
  821. public boolean isTruncateMode() {
  822. return TRUNCATE.equalsIgnoreCase(getValue());
  823. }
  824. /**
  825. * @return true if value is "warn".
  826. */
  827. public boolean isWarnMode() {
  828. return WARN.equalsIgnoreCase(getValue());
  829. }
  830. /**
  831. * @return true if value is "gnu".
  832. */
  833. public boolean isGnuMode() {
  834. return GNU.equalsIgnoreCase(getValue());
  835. }
  836. /**
  837. * @return true if value is "fail".
  838. */
  839. public boolean isFailMode() {
  840. return FAIL.equalsIgnoreCase(getValue());
  841. }
  842. /**
  843. * @return true if value is "omit".
  844. */
  845. public boolean isOmitMode() {
  846. return OMIT.equalsIgnoreCase(getValue());
  847. }
  848. /**
  849. * @return true if value is "posix".
  850. */
  851. public boolean isPosixMode() {
  852. return POSIX.equalsIgnoreCase(getValue());
  853. }
  854. }
  855. /**
  856. * Valid Modes for Compression attribute to Tar Task
  857. *
  858. */
  859. public static final class TarCompressionMethod extends EnumeratedAttribute {
  860. // permissible values for compression attribute
  861. /**
  862. * No compression
  863. */
  864. private static final String NONE = "none";
  865. /**
  866. * GZIP compression
  867. */
  868. private static final String GZIP = "gzip";
  869. /**
  870. * BZIP2 compression
  871. */
  872. private static final String BZIP2 = "bzip2";
  873. /**
  874. * XZ compression
  875. * @since 1.10.1
  876. */
  877. private static final String XZ = "xz";
  878. /**
  879. * Default constructor
  880. */
  881. public TarCompressionMethod() {
  882. super();
  883. setValue(NONE);
  884. }
  885. /**
  886. * Get valid enumeration values.
  887. * @return valid enumeration values
  888. */
  889. @Override
  890. public String[] getValues() {
  891. return new String[] {NONE, GZIP, BZIP2, XZ};
  892. }
  893. /**
  894. * This method wraps the output stream with the
  895. * corresponding compression method
  896. *
  897. * @param ostream output stream
  898. * @return output stream with on-the-fly compression
  899. * @exception IOException thrown if file is not writable
  900. */
  901. private OutputStream compress(final OutputStream ostream)
  902. throws IOException {
  903. final String v = getValue();
  904. if (GZIP.equals(v)) {
  905. return new GZIPOutputStream(ostream);
  906. }
  907. if (XZ.equals(v)) {
  908. return newXZOutputStream(ostream);
  909. }
  910. if (BZIP2.equals(v)) {
  911. ostream.write('B');
  912. ostream.write('Z');
  913. return new CBZip2OutputStream(ostream);
  914. }
  915. return ostream;
  916. }
  917. private static OutputStream newXZOutputStream(OutputStream ostream)
  918. throws BuildException {
  919. try {
  920. Class<?> fClazz = Class.forName("org.tukaani.xz.FilterOptions");
  921. Class<?> oClazz = Class.forName("org.tukaani.xz.LZMA2Options");
  922. Class<? extends OutputStream> sClazz =
  923. Class.forName("org.tukaani.xz.XZOutputStream")
  924. .asSubclass(OutputStream.class);
  925. Constructor<? extends OutputStream> c =
  926. sClazz.getConstructor(OutputStream.class, fClazz);
  927. return c.newInstance(ostream, oClazz.getDeclaredConstructor().newInstance());
  928. } catch (ClassNotFoundException ex) {
  929. throw new BuildException("xz compression requires the XZ for Java library",
  930. ex);
  931. } catch (NoSuchMethodException
  932. | InstantiationException
  933. | IllegalAccessException
  934. | InvocationTargetException
  935. ex) {
  936. throw new BuildException("failed to create XZOutputStream", ex);
  937. }
  938. }
  939. }
  940. }