View Javadoc
1   /*
2    * Copyright (C) 2005-2015 Schlichtherle IT Services.
3    * All rights reserved. Use is subject to license terms.
4    */
5   package net.java.truevfs.comp.tardriver;
6   
7   import java.io.IOException;
8   import java.nio.charset.Charset;
9   import javax.annotation.CheckForNull;
10  import javax.annotation.WillNotClose;
11  import javax.annotation.concurrent.Immutable;
12  import net.java.truecommons.cio.Entry;
13  import static net.java.truecommons.cio.Entry.ALL_POSIX_ACCESS;
14  import static net.java.truecommons.cio.Entry.ALL_POSIX_ENTITIES;
15  import net.java.truecommons.cio.Entry.Access;
16  import static net.java.truecommons.cio.Entry.Access.WRITE;
17  import net.java.truecommons.cio.Entry.PosixEntity;
18  import static net.java.truecommons.cio.Entry.Size.DATA;
19  import net.java.truecommons.cio.Entry.Type;
20  import net.java.truecommons.cio.InputService;
21  import net.java.truecommons.cio.IoBufferPool;
22  import net.java.truecommons.cio.OutputService;
23  import net.java.truecommons.shed.BitField;
24  import net.java.truevfs.kernel.spec.FsAccessOption;
25  import static net.java.truevfs.kernel.spec.FsAccessOption.CACHE;
26  import static net.java.truevfs.kernel.spec.FsAccessOption.COMPRESS;
27  import net.java.truevfs.kernel.spec.FsArchiveDriver;
28  import net.java.truevfs.kernel.spec.FsController;
29  import net.java.truevfs.kernel.spec.FsInputSocketSource;
30  import net.java.truevfs.kernel.spec.FsModel;
31  import net.java.truevfs.kernel.spec.FsNodeName;
32  import net.java.truevfs.kernel.spec.FsOutputSocketSink;
33  import net.java.truevfs.kernel.spec.cio.MultiplexingOutputService;
34  import net.java.truevfs.kernel.spec.sl.IoBufferPoolLocator;
35  import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
36  import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
37  import org.apache.commons.compress.archivers.tar.TarConstants;
38  
39  /**
40   * An archive driver for Tape Archive files (TAR).
41   * By default, TAR files use the US-ASCII character set for the encoding of
42   * entry names.
43   * This configuration pretty much constraints the applicability of this file
44   * format to North American countries.
45   * <p>
46   * Subclasses must be thread-safe and should be immutable!
47   *
48   * @author Christian Schlichtherle
49   */
50  @Immutable
51  public class TarDriver extends FsArchiveDriver<TarDriverEntry> {
52  
53      /**
54       * The character set for entry names and comments, which is the default
55       * character set.
56       */
57      public static final Charset TAR_CHARSET = Charset.defaultCharset();
58  
59      /**
60       * {@inheritDoc}
61       *
62       * @return {@link #TAR_CHARSET}.
63       */
64      @Override
65      public Charset getCharset() {
66          return TAR_CHARSET;
67      }
68  
69      final String getEncoding() {
70          return getCharset().name();
71      }
72  
73      /**
74       * Returns {@code true} if writing PAX headers for non US-ASCII entry names
75       * should be supported or not.
76       * As of TrueVFS 0.10.7, the implementation in {@link TarDriver} returns
77       * {@code true}.
78       * In older versions, the behaviour was as if this method returned
79       * {@code false}.
80       *
81       * @since TrueVFS 0.10.7
82       */
83      public boolean getAddPaxHeaderForNonAsciiNames() {
84          return true;
85      }
86  
87      /**
88       * Returns the method to use for encoding entry names with
89       * {@link TarConstants#NAMELEN} or more characters.
90       * As of TrueVFS 0.10.7, the implementation in {@link TarDriver} returns
91       * {@link TarArchiveOutputStream#LONGFILE_POSIX}.
92       * In older versions, the implementation returned
93       * {@link TarArchiveOutputStream#LONGFILE_GNU}.
94       */
95      public int getLongFileMode() {
96          return TarArchiveOutputStream.LONGFILE_POSIX;
97      }
98  
99      /**
100      * Returns the method to use for writing entries of more than
101      * {@link TarConstants#MAXSIZE} (8 GiB) size.
102      * As of TrueVFS 0.10.7, the implementation in {@link TarDriver} returns
103      * {@link TarArchiveOutputStream#BIGNUMBER_POSIX}.
104      * In older versions, the behaviour was as if this method returned
105      * {@link TarArchiveOutputStream#BIGNUMBER_ERROR}.
106      *
107      * @since TrueVFS 0.10.7
108      */
109     public int getBigNumberMode() {
110         return TarArchiveOutputStream.BIGNUMBER_POSIX;
111     }
112 
113     /**
114      * {@inheritDoc}
115      * <p>
116      * The implementation in the class {@link TarDriver} calls
117      * {@code IoBufferPoolLocator.SINGLETON.get()}.
118      */
119     @Override
120     public IoBufferPool getPool() {
121         return IoBufferPoolLocator.SINGLETON.get();
122     }
123 
124     /**
125      * {@inheritDoc}
126      *
127      * @return The implementation in the class {@link TarDriver} returns
128      *         {@code true} because when reading a TAR file sequentially,
129      *         each TAR entry should &quot;override&quot; any previously read
130      *         TAR entry with an equal name.
131      */
132     @Override
133     public boolean getRedundantContentSupport() {
134         return true;
135     }
136 
137     @Override
138     protected InputService<TarDriverEntry> newInput(
139             final FsModel model,
140             final FsInputSocketSource source)
141     throws IOException {
142         return new TarInputService(model, source, this);
143     }
144 
145     @Override
146     protected OutputService<TarDriverEntry> newOutput(
147             final FsModel model,
148             final FsOutputSocketSink sink,
149             final @CheckForNull @WillNotClose InputService<TarDriverEntry> input)
150     throws IOException {
151         return new MultiplexingOutputService<>(getPool(),
152                 new TarOutputService(model, sink, this));
153     }
154 
155     /**
156      * Clears {@link FsAccessOption#CACHE} in {@code options} before
157      * forwarding the call to {@code controller}.
158      */
159     @Override
160     protected FsInputSocketSource source(
161             BitField<FsAccessOption> options,
162             final FsController controller,
163             final FsNodeName name) {
164         // The target archive file will be only used to extract the TAR entries
165         // to a temporary file, so we don't need to put it into the selective
166         // entry cache.
167         options = options.clear(CACHE);
168         return new FsInputSocketSource(options, controller.input(options, name));
169     }
170 
171     /**
172      * Sets {@link FsAccessOption#COMPRESS} in {@code options} before
173      * forwarding the call to {@code controller}.
174      */
175     @Override
176     protected FsOutputSocketSink sink(
177             BitField<FsAccessOption> options,
178             final FsController controller,
179             final FsNodeName name) {
180         // Leave FsAccessOption.STORE untouched - the driver shall be given
181         // opportunity to get its own preferences to sort out such a conflict.
182         options = options.set(COMPRESS);
183         return new FsOutputSocketSink(options,
184                 controller.output(options, name, null));
185     }
186 
187     @Override
188     public TarDriverEntry newEntry(
189             final BitField<FsAccessOption> options,
190             String name,
191             final Type type,
192             final @CheckForNull Entry template) {
193         name = normalize(name, type);
194         final TarDriverEntry entry;
195         if (template instanceof TarArchiveEntry) {
196             entry = newEntry(name, (TarArchiveEntry) template);
197         } else {
198             entry = newEntry(name);
199             if (null != template) {
200                 entry.setModTime(template.getTime(WRITE));
201                 entry.setSize(template.getSize(DATA));
202                 for (final Access access : ALL_POSIX_ACCESS)
203                     for (final PosixEntity entity : ALL_POSIX_ENTITIES)
204                         entry.setPermitted(access, entity, template.isPermitted(access, entity));
205             }
206         }
207         return entry;
208     }
209 
210     public TarDriverEntry newEntry(String name) {
211         return new TarDriverEntry(name);
212     }
213 
214     public TarDriverEntry newEntry(String name, TarArchiveEntry template) {
215         return new TarDriverEntry(name, template);
216     }
217 }