blob: 993fb92180a4a2ca99ebc80d594718be3ab0e48c [file] [log] [blame]
Torsten Curdtca165392008-07-10 10:17:44 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.apache.commons.compress.archivers.tar;
20
21import java.io.File;
Stefan Bodewigaa9d0bc2011-07-23 05:03:52 +000022import java.nio.ByteBuffer;
Torsten Curdtca165392008-07-10 10:17:44 +000023import java.util.Date;
24import java.util.Locale;
25
26import org.apache.commons.compress.archivers.ArchiveEntry;
27
28/**
Torsten Curdt46ad24d2009-01-08 11:09:25 +000029 * This class represents an entry in a Tar archive. It consists
30 * of the entry's header, as well as the entry's File. Entries
31 * can be instantiated in one of three ways, depending on how
32 * they are to be used.
33 * <p>
34 * TarEntries that are created from the header bytes read from
35 * an archive are instantiated with the TarEntry( byte[] )
36 * constructor. These entries will be used when extracting from
37 * or listing the contents of an archive. These entries have their
38 * header filled in using the header bytes. They also set the File
39 * to null, since they reference an archive entry not a file.
40 * <p>
41 * TarEntries that are created from Files that are to be written
42 * into an archive are instantiated with the TarEntry( File )
43 * constructor. These entries have their header filled in using
44 * the File's information. They also keep a reference to the File
45 * for convenience when writing entries.
46 * <p>
47 * Finally, TarEntries can be constructed from nothing but a name.
48 * This allows the programmer to construct the entry by hand, for
49 * instance when only an InputStream is available for writing to
50 * the archive, and the header information is constructed from
51 * other information. In this case the header fields are set to
52 * defaults and the File is set to null.
Torsten Curdtca165392008-07-10 10:17:44 +000053 *
Torsten Curdt46ad24d2009-01-08 11:09:25 +000054 * <p>
55 * The C structure for a Tar Entry's header is:
56 * <pre>
Torsten Curdtca165392008-07-10 10:17:44 +000057 * struct header {
Sebastian Bazley8118f822009-04-02 23:34:48 +000058 * char name[100]; // TarConstants.NAMELEN - offset 0
59 * char mode[8]; // TarConstants.MODELEN - offset 100
60 * char uid[8]; // TarConstants.UIDLEN - offset 108
61 * char gid[8]; // TarConstants.GIDLEN - offset 116
62 * char size[12]; // TarConstants.SIZELEN - offset 124
63 * char mtime[12]; // TarConstants.MODTIMELEN - offset 136
64 * char chksum[8]; // TarConstants.CHKSUMLEN - offset 148
65 * char linkflag[1]; // - offset 156
66 * char linkname[100]; // TarConstants.NAMELEN - offset 157
Sebastian Bazley24f9c9b2009-04-02 15:19:17 +000067 * The following fields are only present in new-style POSIX tar archives:
Sebastian Bazley8118f822009-04-02 23:34:48 +000068 * char magic[6]; // TarConstants.MAGICLEN - offset 257
69 * char version[2]; // TarConstants.VERSIONLEN - offset 263
70 * char uname[32]; // TarConstants.UNAMELEN - offset 265
71 * char gname[32]; // TarConstants.GNAMELEN - offset 297
72 * char devmajor[8]; // TarConstants.DEVLEN - offset 329
73 * char devminor[8]; // TarConstants.DEVLEN - offset 337
74 * char prefix[155]; // TarConstants.PREFIXLEN - offset 345
75 * // Used if "name" field is not long enough to hold the path
76 * char pad[12]; // NULs - offset 500
Torsten Curdtca165392008-07-10 10:17:44 +000077 * } header;
Sebastian Bazley24f9c9b2009-04-02 15:19:17 +000078 * All unused bytes are set to null.
79 * New-style GNU tar files are slightly different from the above.
Sebastian Bazley16dc21b2012-02-23 00:16:07 +000080 * For values of size larger than 077777777777L (11 7s)
81 * or uid and gid larger than 07777777L (7 7s)
82 * the sign bit of the first byte is set, and the rest of the
83 * field is the binary representation of the number.
84 * See TarUtils.parseOctalOrBinary.
Torsten Curdtca165392008-07-10 10:17:44 +000085 * </pre>
Sebastian Bazley99870ef2009-03-28 00:04:36 +000086 *
Stefan Bodewigaa9d0bc2011-07-23 05:03:52 +000087 * <p>
88 * The C structure for a old GNU Tar Entry's header is:
89 * <pre>
90 * struct oldgnu_header {
91 * char unused_pad1[345]; // TarConstants.PAD1LEN_GNU - offset 0
92 * char atime[12]; // TarConstants.ATIMELEN_GNU - offset 345
93 * char ctime[12]; // TarConstants.CTIMELEN_GNU - offset 357
94 * char offset[12]; // TarConstants.OFFSETLEN_GNU - offset 369
95 * char longnames[4]; // TarConstants.LONGNAMESLEN_GNU - offset 381
96 * char unused_pad2; // TarConstants.PAD2LEN_GNU - offset 385
97 * struct sparse sp[4]; // TarConstants.SPARSELEN_GNU - offset 386
98 * char isextended; // TarConstants.ISEXTENDEDLEN_GNU - offset 482
99 * char realsize[12]; // TarConstants.REALSIZELEN_GNU - offset 483
100 * char unused_pad[17]; // TarConstants.PAD3LEN_GNU - offset 495
101 * };
102 * </pre>
103 * Whereas, "struct sparse" is:
104 * <pre>
105 * struct sparse {
106 * char offset[12]; // offset 0
107 * char numbytes[12]; // offset 12
108 * };
109 * </pre>
110 *
Sebastian Bazley99870ef2009-03-28 00:04:36 +0000111 * @NotThreadSafe
Torsten Curdtca165392008-07-10 10:17:44 +0000112 */
Torsten Curdtca165392008-07-10 10:17:44 +0000113
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000114public class TarArchiveEntry implements TarConstants, ArchiveEntry {
115 /** The entry's name. */
Sebastian Bazley1d556702009-04-02 18:45:02 +0000116 private String name;
Torsten Curdtca165392008-07-10 10:17:44 +0000117
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000118 /** The entry's permission mode. */
119 private int mode;
Torsten Curdtca165392008-07-10 10:17:44 +0000120
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000121 /** The entry's user id. */
122 private int userId;
Torsten Curdtca165392008-07-10 10:17:44 +0000123
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000124 /** The entry's group id. */
125 private int groupId;
Torsten Curdtca165392008-07-10 10:17:44 +0000126
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000127 /** The entry's size. */
128 private long size;
Torsten Curdtca165392008-07-10 10:17:44 +0000129
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000130 /** The entry's modification time. */
131 private long modTime;
Torsten Curdtca165392008-07-10 10:17:44 +0000132
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000133 /** The entry's link flag. */
134 private byte linkFlag;
Torsten Curdtca165392008-07-10 10:17:44 +0000135
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000136 /** The entry's link name. */
Sebastian Bazley1d556702009-04-02 18:45:02 +0000137 private String linkName;
Torsten Curdtca165392008-07-10 10:17:44 +0000138
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000139 /** The entry's magic tag. */
Sebastian Bazley1d556702009-04-02 18:45:02 +0000140 private String magic;
Sebastian Bazley8118f822009-04-02 23:34:48 +0000141 /** The version of the format */
142 private String version;
Torsten Curdtca165392008-07-10 10:17:44 +0000143
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000144 /** The entry's user name. */
Sebastian Bazley1d556702009-04-02 18:45:02 +0000145 private String userName;
Torsten Curdtca165392008-07-10 10:17:44 +0000146
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000147 /** The entry's group name. */
Sebastian Bazley1d556702009-04-02 18:45:02 +0000148 private String groupName;
Torsten Curdtca165392008-07-10 10:17:44 +0000149
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000150 /** The entry's major device number. */
151 private int devMajor;
Torsten Curdtca165392008-07-10 10:17:44 +0000152
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000153 /** The entry's minor device number. */
154 private int devMinor;
Torsten Curdtca165392008-07-10 10:17:44 +0000155
Stefan Bodewigaa9d0bc2011-07-23 05:03:52 +0000156 /** If an extension sparse header follows. */
157 private boolean isExtended;
158
159 /** The entry's real size in case of a sparse file. */
160 private long realSize;
161
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000162 /** The entry's file reference */
163 private File file;
Torsten Curdtca165392008-07-10 10:17:44 +0000164
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000165 /** Maximum length of a user's name in the tar file */
166 public static final int MAX_NAMELEN = 31;
Torsten Curdtca165392008-07-10 10:17:44 +0000167
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000168 /** Default permissions bits for directories */
169 public static final int DEFAULT_DIR_MODE = 040755;
Torsten Curdtca165392008-07-10 10:17:44 +0000170
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000171 /** Default permissions bits for files */
172 public static final int DEFAULT_FILE_MODE = 0100644;
Torsten Curdtca165392008-07-10 10:17:44 +0000173
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000174 /** Convert millis to seconds */
175 public static final int MILLIS_PER_SECOND = 1000;
Torsten Curdtca165392008-07-10 10:17:44 +0000176
177 /**
178 * Construct an empty entry and prepares the header values.
179 */
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000180 private TarArchiveEntry () {
Sebastian Bazley1d556702009-04-02 18:45:02 +0000181 this.magic = MAGIC_POSIX;
Sebastian Bazley8118f822009-04-02 23:34:48 +0000182 this.version = VERSION_POSIX;
Sebastian Bazley1d556702009-04-02 18:45:02 +0000183 this.name = "";
184 this.linkName = "";
Torsten Curdtca165392008-07-10 10:17:44 +0000185
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000186 String user = System.getProperty("user.name", "");
187
188 if (user.length() > MAX_NAMELEN) {
189 user = user.substring(0, MAX_NAMELEN);
Torsten Curdtca165392008-07-10 10:17:44 +0000190 }
191
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000192 this.userId = 0;
193 this.groupId = 0;
Sebastian Bazley1d556702009-04-02 18:45:02 +0000194 this.userName = user;
195 this.groupName = "";
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000196 this.file = null;
Torsten Curdtca165392008-07-10 10:17:44 +0000197 }
198
199 /**
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000200 * Construct an entry with only a name. This allows the programmer
201 * to construct the entry's header "by hand". File is set to null.
Torsten Curdtca165392008-07-10 10:17:44 +0000202 *
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000203 * @param name the entry name
Torsten Curdtca165392008-07-10 10:17:44 +0000204 */
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000205 public TarArchiveEntry(String name) {
Stefan Bodewig3e2ddad2009-08-25 08:11:44 +0000206 this(name, false);
207 }
208
209 /**
210 * Construct an entry with only a name. This allows the programmer
211 * to construct the entry's header "by hand". File is set to null.
212 *
213 * @param name the entry name
214 * @param preserveLeadingSlashes whether to allow leading slashes
215 * in the name.
Sebastian Bazley6209f812010-05-10 17:36:40 +0000216 *
217 * @since Apache Commons Compress 1.1
Stefan Bodewig3e2ddad2009-08-25 08:11:44 +0000218 */
219 public TarArchiveEntry(String name, boolean preserveLeadingSlashes) {
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000220 this();
221
Stefan Bodewig3e2ddad2009-08-25 08:11:44 +0000222 name = normalizeFileName(name, preserveLeadingSlashes);
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000223 boolean isDir = name.endsWith("/");
224
225 this.devMajor = 0;
226 this.devMinor = 0;
Sebastian Bazley1d556702009-04-02 18:45:02 +0000227 this.name = name;
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000228 this.mode = isDir ? DEFAULT_DIR_MODE : DEFAULT_FILE_MODE;
229 this.linkFlag = isDir ? LF_DIR : LF_NORMAL;
230 this.userId = 0;
231 this.groupId = 0;
232 this.size = 0;
233 this.modTime = (new Date()).getTime() / MILLIS_PER_SECOND;
Sebastian Bazley1d556702009-04-02 18:45:02 +0000234 this.linkName = "";
235 this.userName = "";
236 this.groupName = "";
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000237 this.devMajor = 0;
238 this.devMinor = 0;
239
Torsten Curdtca165392008-07-10 10:17:44 +0000240 }
241
242 /**
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000243 * Construct an entry with a name and a link flag.
Torsten Curdtca165392008-07-10 10:17:44 +0000244 *
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000245 * @param name the entry name
246 * @param linkFlag the entry link flag.
Torsten Curdtca165392008-07-10 10:17:44 +0000247 */
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000248 public TarArchiveEntry(String name, byte linkFlag) {
249 this(name);
250 this.linkFlag = linkFlag;
Stefan Bodewig7bb5d882009-10-08 12:07:19 +0000251 if (linkFlag == LF_GNUTYPE_LONGNAME) {
252 magic = MAGIC_GNU;
253 version = VERSION_GNU_SPACE;
254 }
Torsten Curdtca165392008-07-10 10:17:44 +0000255 }
256
257 /**
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000258 * Construct an entry for a file. File is set to file, and the
259 * header is constructed from information from the file.
Sebastian Bazleyfec51a12009-03-31 00:35:56 +0000260 * The name is set from the normalized file path.
Torsten Curdtca165392008-07-10 10:17:44 +0000261 *
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000262 * @param file The file that the entry represents.
Torsten Curdtca165392008-07-10 10:17:44 +0000263 */
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000264 public TarArchiveEntry(File file) {
Stefan Bodewig3e2ddad2009-08-25 08:11:44 +0000265 this(file, normalizeFileName(file.getPath(), false));
Sebastian Bazleyfec51a12009-03-31 00:35:56 +0000266 }
Stefan Bodewigaa9d0bc2011-07-23 05:03:52 +0000267
Sebastian Bazleyfec51a12009-03-31 00:35:56 +0000268 /**
269 * Construct an entry for a file. File is set to file, and the
270 * header is constructed from information from the file.
271 *
272 * @param file The file that the entry represents.
273 * @param fileName the name to be used for the entry.
274 */
275 public TarArchiveEntry(File file, String fileName) {
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000276 this();
Torsten Curdtca165392008-07-10 10:17:44 +0000277
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000278 this.file = file;
Torsten Curdtca165392008-07-10 10:17:44 +0000279
Sebastian Bazley1d556702009-04-02 18:45:02 +0000280 this.linkName = "";
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000281
282 if (file.isDirectory()) {
283 this.mode = DEFAULT_DIR_MODE;
284 this.linkFlag = LF_DIR;
285
Sebastian Bazley1d556702009-04-02 18:45:02 +0000286 int nameLength = fileName.length();
Christian Grobmeier3bb92822009-05-04 09:15:01 +0000287 if (nameLength == 0 || fileName.charAt(nameLength - 1) != '/') {
Sebastian Bazley1d556702009-04-02 18:45:02 +0000288 this.name = fileName + "/";
289 } else {
Stefan Bodewigaa9d0bc2011-07-23 05:03:52 +0000290 this.name = fileName;
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000291 }
Stefan Bodewigc013e282009-03-18 04:38:47 +0000292 this.size = 0;
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000293 } else {
294 this.mode = DEFAULT_FILE_MODE;
295 this.linkFlag = LF_NORMAL;
Stefan Bodewigc013e282009-03-18 04:38:47 +0000296 this.size = file.length();
Sebastian Bazley1d556702009-04-02 18:45:02 +0000297 this.name = fileName;
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000298 }
299
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000300 this.modTime = file.lastModified() / MILLIS_PER_SECOND;
301 this.devMajor = 0;
302 this.devMinor = 0;
Torsten Curdtca165392008-07-10 10:17:44 +0000303 }
304
305 /**
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000306 * Construct an entry from an archive's header bytes. File is set
307 * to null.
Torsten Curdtca165392008-07-10 10:17:44 +0000308 *
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000309 * @param headerBuf The header bytes from a tar archive entry.
Sebastian Bazleyb23b5c82012-02-23 00:57:54 +0000310 * @throws IllegalArgumentException if any of the numeric fields have an invalid format
Torsten Curdtca165392008-07-10 10:17:44 +0000311 */
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000312 public TarArchiveEntry(byte[] headerBuf) {
313 this();
314 parseTarHeader(headerBuf);
Torsten Curdtca165392008-07-10 10:17:44 +0000315 }
316
317 /**
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000318 * Determine if the two entries are equal. Equality is determined
319 * by the header names being equal.
Torsten Curdtca165392008-07-10 10:17:44 +0000320 *
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000321 * @param it Entry to be checked for equality.
322 * @return True if the entries are equal.
Torsten Curdtca165392008-07-10 10:17:44 +0000323 */
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000324 public boolean equals(TarArchiveEntry it) {
325 return getName().equals(it.getName());
Torsten Curdtca165392008-07-10 10:17:44 +0000326 }
327
328 /**
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000329 * Determine if the two entries are equal. Equality is determined
330 * by the header names being equal.
Torsten Curdtca165392008-07-10 10:17:44 +0000331 *
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000332 * @param it Entry to be checked for equality.
333 * @return True if the entries are equal.
Torsten Curdtca165392008-07-10 10:17:44 +0000334 */
Stefan Bodewig46628ef2011-08-06 16:30:40 +0000335 @Override
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000336 public boolean equals(Object it) {
337 if (it == null || getClass() != it.getClass()) {
338 return false;
339 }
340 return equals((TarArchiveEntry) it);
Torsten Curdtca165392008-07-10 10:17:44 +0000341 }
342
343 /**
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000344 * Hashcodes are based on entry names.
Torsten Curdtca165392008-07-10 10:17:44 +0000345 *
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000346 * @return the entry hashcode
Torsten Curdtca165392008-07-10 10:17:44 +0000347 */
Stefan Bodewig46628ef2011-08-06 16:30:40 +0000348 @Override
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000349 public int hashCode() {
350 return getName().hashCode();
Torsten Curdtca165392008-07-10 10:17:44 +0000351 }
352
353 /**
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000354 * Determine if the given entry is a descendant of this entry.
355 * Descendancy is determined by the name of the descendant
356 * starting with this entry's name.
Torsten Curdtca165392008-07-10 10:17:44 +0000357 *
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000358 * @param desc Entry to be checked as a descendent of this.
359 * @return True if entry is a descendant of this.
Torsten Curdtca165392008-07-10 10:17:44 +0000360 */
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000361 public boolean isDescendent(TarArchiveEntry desc) {
362 return desc.getName().startsWith(getName());
Torsten Curdtca165392008-07-10 10:17:44 +0000363 }
364
365 /**
366 * Get this entry's name.
367 *
368 * @return This entry's name.
369 */
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000370 public String getName() {
371 return name.toString();
Torsten Curdtca165392008-07-10 10:17:44 +0000372 }
373
374 /**
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000375 * Set this entry's name.
Torsten Curdtca165392008-07-10 10:17:44 +0000376 *
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000377 * @param name This entry's new name.
Torsten Curdtca165392008-07-10 10:17:44 +0000378 */
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000379 public void setName(String name) {
Stefan Bodewig3e2ddad2009-08-25 08:11:44 +0000380 this.name = normalizeFileName(name, false);
Torsten Curdtca165392008-07-10 10:17:44 +0000381 }
382
383 /**
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000384 * Set the mode for this entry
Torsten Curdtca165392008-07-10 10:17:44 +0000385 *
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000386 * @param mode the mode for this entry
Torsten Curdtca165392008-07-10 10:17:44 +0000387 */
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000388 public void setMode(int mode) {
389 this.mode = mode;
Torsten Curdtca165392008-07-10 10:17:44 +0000390 }
391
392 /**
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000393 * Get this entry's link name.
Torsten Curdtca165392008-07-10 10:17:44 +0000394 *
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000395 * @return This entry's link name.
Torsten Curdtca165392008-07-10 10:17:44 +0000396 */
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000397 public String getLinkName() {
398 return linkName.toString();
Torsten Curdtca165392008-07-10 10:17:44 +0000399 }
400
401 /**
Sebastian Bazley5b85bf12010-05-10 10:59:23 +0000402 * Set this entry's link name.
Sebastian Bazley6209f812010-05-10 17:36:40 +0000403 *
404 * @param link the link name to use.
405 *
406 * @since Apache Commons Compress 1.1
Sebastian Bazley5b85bf12010-05-10 10:59:23 +0000407 */
408 public void setLinkName(String link) {
Sebastian Bazley6209f812010-05-10 17:36:40 +0000409 this.linkName = link;
Sebastian Bazley5b85bf12010-05-10 10:59:23 +0000410 }
411
412 /**
Torsten Curdtca165392008-07-10 10:17:44 +0000413 * Get this entry's user id.
414 *
415 * @return This entry's user id.
416 */
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000417 public int getUserId() {
418 return userId;
419 }
420
421 /**
422 * Set this entry's user id.
423 *
424 * @param userId This entry's new user id.
425 */
426 public void setUserId(int userId) {
427 this.userId = userId;
428 }
429
430 /**
431 * Get this entry's group id.
432 *
433 * @return This entry's group id.
434 */
435 public int getGroupId() {
436 return groupId;
437 }
438
439 /**
440 * Set this entry's group id.
441 *
442 * @param groupId This entry's new group id.
443 */
444 public void setGroupId(int groupId) {
445 this.groupId = groupId;
Torsten Curdtca165392008-07-10 10:17:44 +0000446 }
447
448 /**
449 * Get this entry's user name.
450 *
451 * @return This entry's user name.
452 */
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000453 public String getUserName() {
454 return userName.toString();
Torsten Curdtca165392008-07-10 10:17:44 +0000455 }
456
457 /**
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000458 * Set this entry's user name.
Torsten Curdtca165392008-07-10 10:17:44 +0000459 *
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000460 * @param userName This entry's new user name.
Torsten Curdtca165392008-07-10 10:17:44 +0000461 */
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000462 public void setUserName(String userName) {
Sebastian Bazley1d556702009-04-02 18:45:02 +0000463 this.userName = userName;
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000464 }
465
466 /**
467 * Get this entry's group name.
468 *
469 * @return This entry's group name.
470 */
471 public String getGroupName() {
472 return groupName.toString();
473 }
474
475 /**
476 * Set this entry's group name.
477 *
478 * @param groupName This entry's new group name.
479 */
480 public void setGroupName(String groupName) {
Sebastian Bazley1d556702009-04-02 18:45:02 +0000481 this.groupName = groupName;
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000482 }
483
484 /**
485 * Convenience method to set this entry's group and user ids.
486 *
487 * @param userId This entry's new user id.
488 * @param groupId This entry's new group id.
489 */
490 public void setIds(int userId, int groupId) {
491 setUserId(userId);
492 setGroupId(groupId);
493 }
494
495 /**
496 * Convenience method to set this entry's group and user names.
497 *
498 * @param userName This entry's new user name.
499 * @param groupName This entry's new group name.
500 */
501 public void setNames(String userName, String groupName) {
502 setUserName(userName);
503 setGroupName(groupName);
504 }
505
506 /**
507 * Set this entry's modification time. The parameter passed
508 * to this method is in "Java time".
509 *
510 * @param time This entry's new modification time.
511 */
512 public void setModTime(long time) {
513 modTime = time / MILLIS_PER_SECOND;
514 }
515
516 /**
517 * Set this entry's modification time.
518 *
519 * @param time This entry's new modification time.
520 */
521 public void setModTime(Date time) {
522 modTime = time.getTime() / MILLIS_PER_SECOND;
523 }
524
525 /**
526 * Set this entry's modification time.
527 *
528 * @return time This entry's new modification time.
529 */
530 public Date getModTime() {
531 return new Date(modTime * MILLIS_PER_SECOND);
532 }
533
Sebastian Bazley7b115db2010-03-13 13:44:50 +0000534 /** {@inheritDoc} */
Stefan Bodewig17ffd7f2009-08-01 14:52:15 +0000535 public Date getLastModifiedDate() {
536 return getModTime();
537 }
538
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000539 /**
540 * Get this entry's file.
541 *
542 * @return This entry's file.
543 */
544 public File getFile() {
545 return file;
546 }
547
548 /**
549 * Get this entry's mode.
550 *
551 * @return This entry's mode.
552 */
553 public int getMode() {
554 return mode;
555 }
556
557 /**
558 * Get this entry's file size.
559 *
560 * @return This entry's file size.
561 */
562 public long getSize() {
563 return size;
564 }
565
566 /**
567 * Set this entry's file size.
568 *
569 * @param size This entry's new file size.
Stefan Bodewiga6fbdae2011-12-08 14:03:57 +0000570 * @throws IllegalArgumentException if the size is &lt; 0.
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000571 */
572 public void setSize(long size) {
Stefan Bodewig5dad1122011-12-07 11:34:34 +0000573 if (size < 0){
Stefan Bodewiga6fbdae2011-12-08 14:03:57 +0000574 throw new IllegalArgumentException("Size is out of range: "+size);
Stefan Bodewig5dad1122011-12-07 11:34:34 +0000575 }
576 this.size = size;
577 }
578
579 /**
Stefan Bodewigaa9d0bc2011-07-23 05:03:52 +0000580 * Indicates in case of a sparse file if an extension sparse header
581 * follows.
582 *
583 * @return true if an extension sparse header follows.
584 */
585 public boolean isExtended() {
586 return isExtended;
587 }
588
589 /**
590 * Get this entry's real file size in case of a sparse file.
591 *
592 * @return This entry's real file size.
593 */
594 public long getRealSize() {
595 return realSize;
596 }
597
598 /**
599 * Indicate if this entry is a GNU sparse block
600 *
601 * @return true if this is a sparse extension provided by GNU tar
602 */
603 public boolean isGNUSparse() {
604 return linkFlag == LF_GNUTYPE_SPARSE;
605 }
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000606
607 /**
608 * Indicate if this entry is a GNU long name block
609 *
610 * @return true if this is a long name extension provided by GNU tar
611 */
612 public boolean isGNULongNameEntry() {
613 return linkFlag == LF_GNUTYPE_LONGNAME
Stefan Bodewig75f92f62009-03-17 12:14:17 +0000614 && name.toString().equals(GNU_LONGLINK);
Torsten Curdtca165392008-07-10 10:17:44 +0000615 }
616
617 /**
Sebastian Bazley33cd35e2010-05-09 20:38:20 +0000618 * Check if this is a Pax header.
619 *
620 * @return <code>true</code> if this is a Pax header.
Sebastian Bazley6209f812010-05-10 17:36:40 +0000621 *
622 * @since Apache Commons Compress 1.1
Sebastian Bazley33cd35e2010-05-09 20:38:20 +0000623 */
624 public boolean isPaxHeader(){
Sebastian Bazley05b6eb12010-05-10 01:43:08 +0000625 return linkFlag == LF_PAX_EXTENDED_HEADER_LC
626 || linkFlag == LF_PAX_EXTENDED_HEADER_UC;
627 }
628
629 /**
630 * Check if this is a Pax header.
631 *
632 * @return <code>true</code> if this is a Pax header.
Sebastian Bazley6209f812010-05-10 17:36:40 +0000633 *
634 * @since Apache Commons Compress 1.1
Sebastian Bazley05b6eb12010-05-10 01:43:08 +0000635 */
636 public boolean isGlobalPaxHeader(){
637 return linkFlag == LF_PAX_GLOBAL_EXTENDED_HEADER;
Sebastian Bazley33cd35e2010-05-09 20:38:20 +0000638 }
639
640 /**
Torsten Curdtca165392008-07-10 10:17:44 +0000641 * Return whether or not this entry represents a directory.
642 *
643 * @return True if this entry is a directory.
644 */
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000645 public boolean isDirectory() {
646 if (file != null) {
647 return file.isDirectory();
Torsten Curdtca165392008-07-10 10:17:44 +0000648 }
649
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000650 if (linkFlag == LF_DIR) {
Torsten Curdtca165392008-07-10 10:17:44 +0000651 return true;
652 }
653
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000654 if (getName().endsWith("/")) {
Torsten Curdtca165392008-07-10 10:17:44 +0000655 return true;
656 }
657
658 return false;
659 }
660
661 /**
Stefan Bodewig86aaf842010-10-29 15:03:49 +0000662 * Check if this is a "normal file"
663 *
664 * @since Apache Commons Compress 1.2
665 */
666 public boolean isFile() {
667 if (file != null) {
668 return file.isFile();
669 }
670 if (linkFlag == LF_OLDNORM || linkFlag == LF_NORMAL) {
671 return true;
672 }
673 return !getName().endsWith("/");
674 }
675
676 /**
677 * Check if this is a symbolic link entry.
678 *
679 * @since Apache Commons Compress 1.2
680 */
681 public boolean isSymbolicLink() {
682 return linkFlag == LF_SYMLINK;
683 }
684
685 /**
686 * Check if this is a link entry.
687 *
688 * @since Apache Commons Compress 1.2
689 */
690 public boolean isLink() {
691 return linkFlag == LF_LINK;
692 }
693
694 /**
695 * Check if this is a character device entry.
696 *
697 * @since Apache Commons Compress 1.2
698 */
699 public boolean isCharacterDevice() {
700 return linkFlag == LF_CHR;
701 }
702
703 /**
704 * Check if this is a block device entry.
705 *
706 * @since Apache Commons Compress 1.2
707 */
708 public boolean isBlockDevice() {
709 return linkFlag == LF_BLK;
710 }
711
712 /**
713 * Check if this is a FIFO (pipe) entry.
714 *
715 * @since Apache Commons Compress 1.2
716 */
717 public boolean isFIFO() {
718 return linkFlag == LF_FIFO;
719 }
720
721 /**
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000722 * If this entry represents a file, and the file is a directory, return
723 * an array of TarEntries for this entry's children.
Torsten Curdtca165392008-07-10 10:17:44 +0000724 *
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000725 * @return An array of TarEntry's for this entry's children.
Torsten Curdtca165392008-07-10 10:17:44 +0000726 */
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000727 public TarArchiveEntry[] getDirectoryEntries() {
728 if (file == null || !file.isDirectory()) {
729 return new TarArchiveEntry[0];
730 }
731
732 String[] list = file.list();
733 TarArchiveEntry[] result = new TarArchiveEntry[list.length];
734
735 for (int i = 0; i < list.length; ++i) {
736 result[i] = new TarArchiveEntry(new File(file, list[i]));
737 }
738
739 return result;
Torsten Curdtca165392008-07-10 10:17:44 +0000740 }
741
742 /**
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000743 * Write an entry's header information to a header buffer.
Torsten Curdtca165392008-07-10 10:17:44 +0000744 *
Stefan Bodewiga6fbdae2011-12-08 14:03:57 +0000745 * <p>This method does not use the star/GNU tar/BSD tar extensions.</p>
746 *
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000747 * @param outbuf The tar entry header buffer to fill in.
Torsten Curdtca165392008-07-10 10:17:44 +0000748 */
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000749 public void writeEntryHeader(byte[] outbuf) {
Stefan Bodewiga6fbdae2011-12-08 14:03:57 +0000750 writeEntryHeader(outbuf, false);
751 }
752
753 /**
754 * Write an entry's header information to a header buffer.
755 *
756 * @param outbuf The tar entry header buffer to fill in.
757 * @param starMode whether to use the star/GNU tar/BSD tar
Stefan Bodewigbe286da2012-03-05 19:34:12 +0000758 * extension for numeric fields if their value doesn't fit in the
759 * maximum size of standard tar archives
Stefan Bodewiga6fbdae2011-12-08 14:03:57 +0000760 * @since Apache Commons Compress 1.4
761 */
762 public void writeEntryHeader(byte[] outbuf, boolean starMode) {
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000763 int offset = 0;
764
Sebastian Bazley1d556702009-04-02 18:45:02 +0000765 offset = TarUtils.formatNameBytes(name, outbuf, offset, NAMELEN);
Stefan Bodewigbe286da2012-03-05 19:34:12 +0000766 offset = writeEntryHeaderField(mode, outbuf, offset, MODELEN, starMode);
767 offset = writeEntryHeaderField(userId, outbuf, offset, UIDLEN,
768 starMode);
769 offset = writeEntryHeaderField(groupId, outbuf, offset, GIDLEN,
770 starMode);
771 offset = writeEntryHeaderField(size, outbuf, offset, SIZELEN, starMode);
772 offset = writeEntryHeaderField(modTime, outbuf, offset, MODTIMELEN,
773 starMode);
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000774
775 int csOffset = offset;
776
777 for (int c = 0; c < CHKSUMLEN; ++c) {
778 outbuf[offset++] = (byte) ' ';
779 }
780
781 outbuf[offset++] = linkFlag;
Sebastian Bazley1d556702009-04-02 18:45:02 +0000782 offset = TarUtils.formatNameBytes(linkName, outbuf, offset, NAMELEN);
783 offset = TarUtils.formatNameBytes(magic, outbuf, offset, MAGICLEN);
Sebastian Bazley8118f822009-04-02 23:34:48 +0000784 offset = TarUtils.formatNameBytes(version, outbuf, offset, VERSIONLEN);
Sebastian Bazley1d556702009-04-02 18:45:02 +0000785 offset = TarUtils.formatNameBytes(userName, outbuf, offset, UNAMELEN);
786 offset = TarUtils.formatNameBytes(groupName, outbuf, offset, GNAMELEN);
Stefan Bodewigbe286da2012-03-05 19:34:12 +0000787 offset = writeEntryHeaderField(devMajor, outbuf, offset, DEVLEN,
788 starMode);
789 offset = writeEntryHeaderField(devMinor, outbuf, offset, DEVLEN,
790 starMode);
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000791
792 while (offset < outbuf.length) {
793 outbuf[offset++] = 0;
794 }
795
796 long chk = TarUtils.computeCheckSum(outbuf);
797
Sebastian Bazley1d556702009-04-02 18:45:02 +0000798 TarUtils.formatCheckSumOctalBytes(chk, outbuf, csOffset, CHKSUMLEN);
Torsten Curdtca165392008-07-10 10:17:44 +0000799 }
800
Stefan Bodewigbe286da2012-03-05 19:34:12 +0000801 private int writeEntryHeaderField(long value, byte[] outbuf, int offset,
802 int length, boolean starMode) {
803 if (!starMode && (value < 0
804 || value >= (1l << (3 * (length - 1))))) {
805 // value doesn't fit into field when written as octal
806 // number, will be written to PAX header or causes an
807 // error
808 return TarUtils.formatLongOctalBytes(0, outbuf, offset, length);
809 }
810 return TarUtils.formatLongOctalOrBinaryBytes(value, outbuf, offset,
811 length);
812 }
813
Torsten Curdtca165392008-07-10 10:17:44 +0000814 /**
815 * Parse an entry's header information from a header buffer.
816 *
817 * @param header The tar entry header buffer to get information from.
Sebastian Bazleyb23b5c82012-02-23 00:57:54 +0000818 * @throws IllegalArgumentException if any of the numeric fields have an invalid format
Torsten Curdtca165392008-07-10 10:17:44 +0000819 */
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000820 public void parseTarHeader(byte[] header) {
Torsten Curdtca165392008-07-10 10:17:44 +0000821 int offset = 0;
822
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000823 name = TarUtils.parseName(header, offset, NAMELEN);
Torsten Curdtca165392008-07-10 10:17:44 +0000824 offset += NAMELEN;
Stefan Bodewig6c71a2b2012-03-04 07:28:12 +0000825 mode = (int) TarUtils.parseOctalOrBinary(header, offset, MODELEN);
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000826 offset += MODELEN;
Sebastian Bazley7ee6b672012-02-22 23:42:49 +0000827 userId = (int) TarUtils.parseOctalOrBinary(header, offset, UIDLEN);
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000828 offset += UIDLEN;
Sebastian Bazley7ee6b672012-02-22 23:42:49 +0000829 groupId = (int) TarUtils.parseOctalOrBinary(header, offset, GIDLEN);
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000830 offset += GIDLEN;
Stefan Bodewig68465642011-12-05 09:58:04 +0000831 size = TarUtils.parseOctalOrBinary(header, offset, SIZELEN);
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000832 offset += SIZELEN;
Stefan Bodewig6c71a2b2012-03-04 07:28:12 +0000833 modTime = TarUtils.parseOctalOrBinary(header, offset, MODTIMELEN);
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000834 offset += MODTIMELEN;
835 offset += CHKSUMLEN;
836 linkFlag = header[offset++];
837 linkName = TarUtils.parseName(header, offset, NAMELEN);
Torsten Curdtca165392008-07-10 10:17:44 +0000838 offset += NAMELEN;
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000839 magic = TarUtils.parseName(header, offset, MAGICLEN);
840 offset += MAGICLEN;
Sebastian Bazley8118f822009-04-02 23:34:48 +0000841 version = TarUtils.parseName(header, offset, VERSIONLEN);
842 offset += VERSIONLEN;
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000843 userName = TarUtils.parseName(header, offset, UNAMELEN);
844 offset += UNAMELEN;
845 groupName = TarUtils.parseName(header, offset, GNAMELEN);
846 offset += GNAMELEN;
Stefan Bodewig6c71a2b2012-03-04 07:28:12 +0000847 devMajor = (int) TarUtils.parseOctalOrBinary(header, offset, DEVLEN);
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000848 offset += DEVLEN;
Stefan Bodewig6c71a2b2012-03-04 07:28:12 +0000849 devMinor = (int) TarUtils.parseOctalOrBinary(header, offset, DEVLEN);
Sebastian Bazley33cd35e2010-05-09 20:38:20 +0000850 offset += DEVLEN;
Stefan Bodewigaa9d0bc2011-07-23 05:03:52 +0000851
852 int type = evaluateType(header);
853 switch (type) {
854 case FORMAT_OLDGNU: {
855 offset += ATIMELEN_GNU;
856 offset += CTIMELEN_GNU;
857 offset += OFFSETLEN_GNU;
858 offset += LONGNAMESLEN_GNU;
859 offset += PAD2LEN_GNU;
860 offset += SPARSELEN_GNU;
861 isExtended = TarUtils.parseBoolean(header, offset);
862 offset += ISEXTENDEDLEN_GNU;
863 realSize = TarUtils.parseOctal(header, offset, REALSIZELEN_GNU);
864 offset += REALSIZELEN_GNU;
865 break;
Sebastian Bazley5b25f6c2010-05-10 09:36:15 +0000866 }
Stefan Bodewigaa9d0bc2011-07-23 05:03:52 +0000867 case FORMAT_POSIX:
868 default: {
869 String prefix = TarUtils.parseName(header, offset, PREFIXLEN);
870 // SunOS tar -E does not add / to directory names, so fix
871 // up to be consistent
872 if (isDirectory() && !name.endsWith("/")){
873 name = name + "/";
874 }
875 if (prefix.length() > 0){
876 name = prefix + "/" + name;
877 }
878 }
Sebastian Bazley33cd35e2010-05-09 20:38:20 +0000879 }
Torsten Curdtca165392008-07-10 10:17:44 +0000880 }
Stefan Bodewig32eea1e2009-03-17 12:53:22 +0000881
882 /**
883 * Strips Windows' drive letter as well as any leading slashes,
884 * turns path separators into forward slahes.
885 */
Stefan Bodewig3e2ddad2009-08-25 08:11:44 +0000886 private static String normalizeFileName(String fileName,
887 boolean preserveLeadingSlashes) {
Stefan Bodewigbed564b2010-02-18 12:34:02 +0000888 String osname = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
Stefan Bodewig32eea1e2009-03-17 12:53:22 +0000889
890 if (osname != null) {
891
892 // Strip off drive letters!
893 // REVIEW Would a better check be "(File.separator == '\')"?
894
895 if (osname.startsWith("windows")) {
896 if (fileName.length() > 2) {
897 char ch1 = fileName.charAt(0);
898 char ch2 = fileName.charAt(1);
899
900 if (ch2 == ':'
901 && ((ch1 >= 'a' && ch1 <= 'z')
902 || (ch1 >= 'A' && ch1 <= 'Z'))) {
903 fileName = fileName.substring(2);
904 }
905 }
906 } else if (osname.indexOf("netware") > -1) {
907 int colon = fileName.indexOf(':');
908 if (colon != -1) {
909 fileName = fileName.substring(colon + 1);
910 }
911 }
912 }
913
914 fileName = fileName.replace(File.separatorChar, '/');
915
916 // No absolute pathnames
917 // Windows (and Posix?) paths can start with "\\NetworkDrive\",
918 // so we loop on starting /'s.
Stefan Bodewig3e2ddad2009-08-25 08:11:44 +0000919 while (!preserveLeadingSlashes && fileName.startsWith("/")) {
Stefan Bodewig32eea1e2009-03-17 12:53:22 +0000920 fileName = fileName.substring(1);
921 }
922 return fileName;
923 }
Stefan Bodewigaa9d0bc2011-07-23 05:03:52 +0000924
925 /**
926 * Evaluate an entry's header format from a header buffer.
927 *
928 * @param header The tar entry header buffer to evaluate the format for.
929 * @return format type
930 */
931 private int evaluateType(byte[] header) {
932 final ByteBuffer magic = ByteBuffer.wrap(header, MAGIC_OFFSET, MAGICLEN);
933 if (magic.compareTo(ByteBuffer.wrap(MAGIC_GNU.getBytes())) == 0)
934 return FORMAT_OLDGNU;
935 if (magic.compareTo(ByteBuffer.wrap(MAGIC_POSIX.getBytes())) == 0)
936 return FORMAT_POSIX;
937 return 0;
938 }
Torsten Curdtca165392008-07-10 10:17:44 +0000939}
Torsten Curdt46ad24d2009-01-08 11:09:25 +0000940