001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.commons.vfs.provider.tar;
018:
019: import java.io.File;
020: import java.util.Date;
021: import java.util.Locale;
022:
023: /**
024: * This class represents an entry in a Tar archive. It consists of the entry's
025: * header, as well as the entry's File. Entries can be instantiated in one of
026: * three ways, depending on how they are to be used. <p>
027: *
028: * TarEntries that are created from the header bytes read from an archive are
029: * instantiated with the TarEntry( byte[] ) constructor. These entries will be
030: * used when extracting from or listing the contents of an archive. These
031: * entries have their header filled in using the header bytes. They also set the
032: * File to null, since they reference an archive entry not a file. <p>
033: *
034: * TarEntries that are created from Files that are to be written into an archive
035: * are instantiated with the TarEntry( File ) constructor. These entries have
036: * their header filled in using the File's information. They also keep a
037: * reference to the File for convenience when writing entries. <p>
038: *
039: * Finally, TarEntries can be constructed from nothing but a name. This allows
040: * the programmer to construct the entry by hand, for instance when only an
041: * InputStream is available for writing to the archive, and the header
042: * information is constructed from other information. In this case the header
043: * fields are set to defaults and the File is set to null. <p>
044: *
045: * The C structure for a Tar Entry's header is: <pre>
046: * struct header {
047: * char name[NAMSIZ];
048: * char mode[8];
049: * char uid[8];
050: * char gid[8];
051: * char size[12];
052: * char mtime[12];
053: * char chksum[8];
054: * char linkflag;
055: * char linkname[NAMSIZ];
056: * char magic[8];
057: * char uname[TUNMLEN];
058: * char gname[TGNMLEN];
059: * char devmajor[8];
060: * char devminor[8];
061: * } header;
062: * </pre>
063: *
064: * @author <a href="mailto:time@ice.com">Timothy Gerard Endres</a>
065: * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
066: * @author <a href="mailto:peter@apache.org">Peter Donald</a>
067: * @version $Revision: 480428 $ $Date: 2006-11-28 22:15:24 -0800 (Tue, 28 Nov 2006) $
068: * @see TarInputStream
069: * @see TarOutputStream
070: */
071: class TarEntry {
072: /**
073: * The length of the name field in a header buffer.
074: */
075: public static final int NAMELEN = 100;
076:
077: /**
078: * The entry's modification time.
079: */
080: private int m_checkSum;
081:
082: /**
083: * The entry's group name.
084: */
085: private int m_devMajor;
086:
087: /**
088: * The entry's major device number.
089: */
090: private int m_devMinor;
091:
092: /**
093: * The entry's minor device number.
094: */
095: private File m_file;
096:
097: /**
098: * The entry's user id.
099: */
100: private int m_groupID;
101:
102: /**
103: * The entry's user name.
104: */
105: private StringBuffer m_groupName;
106:
107: /**
108: * The entry's checksum.
109: */
110: private byte m_linkFlag;
111:
112: /**
113: * The entry's link flag.
114: */
115: private StringBuffer m_linkName;
116:
117: /**
118: * The entry's link name.
119: */
120: private StringBuffer m_magic;
121:
122: /**
123: * The entry's size.
124: */
125: private long m_modTime;
126:
127: /**
128: * The entry's name.
129: */
130: private int m_mode;
131:
132: private StringBuffer m_name;
133:
134: /**
135: * The entry's group id.
136: */
137: private long m_size;
138:
139: /**
140: * The entry's permission mode.
141: */
142: private int m_userID;
143:
144: /**
145: * The entry's magic tag.
146: */
147: private StringBuffer m_userName;
148:
149: /**
150: * Construct an entry with only a name. This allows the programmer to
151: * construct the entry's header "by hand". File is set to null.
152: *
153: * @param name the name of the entry
154: */
155: TarEntry(final String name) {
156: this ();
157:
158: final boolean isDir = name.endsWith("/");
159:
160: m_name = new StringBuffer(name);
161: m_mode = isDir ? 040755 : 0100644;
162: m_linkFlag = isDir ? TarConstants.LF_DIR
163: : TarConstants.LF_NORMAL;
164: m_modTime = (new Date()).getTime() / 1000;
165: m_linkName = new StringBuffer("");
166: m_userName = new StringBuffer("");
167: m_groupName = new StringBuffer("");
168: }
169:
170: /**
171: * Construct an entry with a name an a link flag.
172: *
173: * @param name Description of Parameter
174: * @param linkFlag Description of Parameter
175: */
176: TarEntry(final String name, final byte linkFlag) {
177: this (name);
178: m_linkFlag = linkFlag;
179: }
180:
181: /**
182: * Construct an entry for a file. File is set to file, and the header is
183: * constructed from information from the file.
184: *
185: * @param file The file that the entry represents.
186: */
187: TarEntry(final File file) {
188: this ();
189:
190: m_file = file;
191:
192: String name = file.getPath();
193:
194: // Strip off drive letters!
195: final String osName = System.getProperty("os.name")
196: .toLowerCase(Locale.US);
197: if (-1 != osName.indexOf("netware")) {
198: if (name.length() > 2) {
199: final char ch1 = name.charAt(0);
200: final char ch2 = name.charAt(1);
201:
202: if (ch2 == ':'
203: && ((ch1 >= 'a' && ch1 <= 'z') || (ch1 >= 'A' && ch1 <= 'Z'))) {
204: name = name.substring(2);
205: }
206: }
207: } else if (-1 != osName.indexOf("netware")) {
208: final int colon = name.indexOf(':');
209: if (colon != -1) {
210: name = name.substring(colon + 1);
211: }
212: }
213:
214: name = name.replace(File.separatorChar, '/');
215:
216: // No absolute pathnames
217: // Windows (and Posix?) paths can start with "\\NetworkDrive\",
218: // so we loop on starting /'s.
219: while (name.startsWith("/")) {
220: name = name.substring(1);
221: }
222:
223: m_linkName = new StringBuffer("");
224: m_name = new StringBuffer(name);
225:
226: if (file.isDirectory()) {
227: m_mode = 040755;
228: m_linkFlag = TarConstants.LF_DIR;
229:
230: if (m_name.charAt(m_name.length() - 1) != '/') {
231: m_name.append("/");
232: }
233: } else {
234: m_mode = 0100644;
235: m_linkFlag = TarConstants.LF_NORMAL;
236: }
237:
238: m_size = file.length();
239: m_modTime = file.lastModified() / 1000;
240: m_checkSum = 0;
241: m_devMajor = 0;
242: m_devMinor = 0;
243: }
244:
245: /**
246: * Construct an entry from an archive's header bytes. File is set to null.
247: *
248: * @param header The header bytes from a tar archive entry.
249: */
250: TarEntry(final byte[] header) {
251: this ();
252: parseTarHeader(header);
253: }
254:
255: /**
256: * Construct an empty entry and prepares the header values.
257: */
258: private TarEntry() {
259: m_magic = new StringBuffer(TarConstants.TMAGIC);
260: m_name = new StringBuffer();
261: m_linkName = new StringBuffer();
262:
263: String user = System.getProperty("user.name", "");
264: if (user.length() > 31) {
265: user = user.substring(0, 31);
266: }
267:
268: m_userName = new StringBuffer(user);
269: m_groupName = new StringBuffer("");
270: }
271:
272: /**
273: * Set this entry's group id.
274: *
275: * @param groupId This entry's new group id.
276: */
277: public void setGroupID(final int groupId) {
278: m_groupID = groupId;
279: }
280:
281: /**
282: * Set this entry's group id.
283: *
284: * @param groupId This entry's new group id.
285: * @deprecated Use setGroupID() instead
286: * @see #setGroupID(int)
287: */
288: public void setGroupId(final int groupId) {
289: m_groupID = groupId;
290: }
291:
292: /**
293: * Set this entry's group name.
294: *
295: * @param groupName This entry's new group name.
296: */
297: public void setGroupName(final String groupName) {
298: m_groupName = new StringBuffer(groupName);
299: }
300:
301: /**
302: * Set this entry's modification time. The parameter passed to this method
303: * is in "Java time".
304: *
305: * @param time This entry's new modification time.
306: */
307: public void setModTime(final long time) {
308: m_modTime = time / 1000;
309: }
310:
311: /**
312: * Set this entry's modification time.
313: *
314: * @param time This entry's new modification time.
315: */
316: public void setModTime(final Date time) {
317: m_modTime = time.getTime() / 1000;
318: }
319:
320: /**
321: * Set the mode for this entry
322: *
323: * @param mode The new Mode value
324: */
325: public void setMode(final int mode) {
326: m_mode = mode;
327: }
328:
329: /**
330: * Set this entry's name.
331: *
332: * @param name This entry's new name.
333: */
334: public void setName(final String name) {
335: m_name = new StringBuffer(name);
336: }
337:
338: /**
339: * Set this entry's file size.
340: *
341: * @param size This entry's new file size.
342: */
343: public void setSize(final long size) {
344: m_size = size;
345: }
346:
347: /**
348: * Set this entry's user id.
349: *
350: * @param userId This entry's new user id.
351: */
352: public void setUserID(final int userId) {
353: m_userID = userId;
354: }
355:
356: /**
357: * Set this entry's user id.
358: *
359: * @param userId This entry's new user id.
360: * @deprecated Use setUserID() instead
361: * @see #setUserID(int)
362: */
363: public void setUserId(final int userId) {
364: m_userID = userId;
365: }
366:
367: /**
368: * Set this entry's user name.
369: *
370: * @param userName This entry's new user name.
371: */
372: public void setUserName(final String userName) {
373: m_userName = new StringBuffer(userName);
374: }
375:
376: /**
377: * If this entry represents a file, and the file is a directory, return an
378: * array of TarEntries for this entry's children.
379: *
380: * @return An array of TarEntry's for this entry's children.
381: */
382: public TarEntry[] getDirectoryEntries() {
383: if (null == m_file || !m_file.isDirectory()) {
384: return new TarEntry[0];
385: }
386:
387: final String[] list = m_file.list();
388: final TarEntry[] result = new TarEntry[list.length];
389:
390: for (int i = 0; i < list.length; ++i) {
391: result[i] = new TarEntry(new File(m_file, list[i]));
392: }
393:
394: return result;
395: }
396:
397: /**
398: * Get this entry's file.
399: *
400: * @return This entry's file.
401: */
402: public File getFile() {
403: return m_file;
404: }
405:
406: /**
407: * Get this entry's group id.
408: *
409: * @return This entry's group id.
410: * @deprecated Use getGroupID() instead
411: * @see #getGroupID()
412: */
413: public int getGroupId() {
414: return m_groupID;
415: }
416:
417: /**
418: * Get this entry's group id.
419: *
420: * @return This entry's group id.
421: */
422: public int getGroupID() {
423: return m_groupID;
424: }
425:
426: /**
427: * Get this entry's group name.
428: *
429: * @return This entry's group name.
430: */
431: public String getGroupName() {
432: return m_groupName.toString();
433: }
434:
435: /**
436: * Set this entry's modification time.
437: *
438: * @return The ModTime value
439: */
440: public Date getModTime() {
441: return new Date(m_modTime * 1000);
442: }
443:
444: /**
445: * Get this entry's mode.
446: *
447: * @return This entry's mode.
448: */
449: public int getMode() {
450: return m_mode;
451: }
452:
453: /**
454: * Get this entry's name.
455: *
456: * @return This entry's name.
457: */
458: public String getName() {
459: return m_name.toString();
460: }
461:
462: /**
463: * Get this entry's file size.
464: *
465: * @return This entry's file size.
466: */
467: public long getSize() {
468: return m_size;
469: }
470:
471: /**
472: * Get this entry's checksum.
473: *
474: * @return This entry's checksum.
475: */
476: public int getCheckSum() {
477: return m_checkSum;
478: }
479:
480: /**
481: * Get this entry's user id.
482: *
483: * @return This entry's user id.
484: * @deprecated Use getUserID() instead
485: * @see #getUserID()
486: */
487: public int getUserId() {
488: return m_userID;
489: }
490:
491: /**
492: * Get this entry's user id.
493: *
494: * @return This entry's user id.
495: */
496: public int getUserID() {
497: return m_userID;
498: }
499:
500: /**
501: * Get this entry's user name.
502: *
503: * @return This entry's user name.
504: */
505: public String getUserName() {
506: return m_userName.toString();
507: }
508:
509: /**
510: * Determine if the given entry is a descendant of this entry. Descendancy
511: * is determined by the name of the descendant starting with this entry's
512: * name.
513: *
514: * @param desc Entry to be checked as a descendent of
515: * @return True if entry is a descendant of
516: */
517: public boolean isDescendent(final TarEntry desc) {
518: return desc.getName().startsWith(getName());
519: }
520:
521: /**
522: * Return whether or not this entry represents a directory.
523: *
524: * @return True if this entry is a directory.
525: */
526: public boolean isDirectory() {
527: if (m_file != null) {
528: return m_file.isDirectory();
529: }
530:
531: if (m_linkFlag == TarConstants.LF_DIR) {
532: return true;
533: }
534:
535: if (getName().endsWith("/")) {
536: return true;
537: }
538:
539: return false;
540: }
541:
542: /**
543: * Indicate if this entry is a GNU long name block
544: *
545: * @return true if this is a long name extension provided by GNU tar
546: */
547: public boolean isGNULongNameEntry() {
548: return m_linkFlag == TarConstants.LF_GNUTYPE_LONGNAME
549: && m_name.toString().equals(TarConstants.GNU_LONGLINK);
550: }
551:
552: /**
553: * Determine if the two entries are equal. Equality is determined by the
554: * header names being equal.
555: *
556: * @param other Entry to be checked for equality.
557: * @return True if the entries are equal.
558: */
559: public boolean equals(final TarEntry other) {
560: return getName().equals(other.getName());
561: }
562:
563: /**
564: * Parse an entry's header information from a header buffer.
565: *
566: * @param header The tar entry header buffer to get information from.
567: */
568: private void parseTarHeader(final byte[] header) {
569: int offset = 0;
570:
571: m_name = TarUtils.parseName(header, offset, NAMELEN);
572: offset += NAMELEN;
573: m_mode = (int) TarUtils.parseOctal(header, offset,
574: TarConstants.MODELEN);
575: offset += TarConstants.MODELEN;
576: m_userID = (int) TarUtils.parseOctal(header, offset,
577: TarConstants.UIDLEN);
578: offset += TarConstants.UIDLEN;
579: m_groupID = (int) TarUtils.parseOctal(header, offset,
580: TarConstants.GIDLEN);
581: offset += TarConstants.GIDLEN;
582: m_size = TarUtils.parseOctal(header, offset,
583: TarConstants.SIZELEN);
584: offset += TarConstants.SIZELEN;
585: m_modTime = TarUtils.parseOctal(header, offset,
586: TarConstants.MODTIMELEN);
587: offset += TarConstants.MODTIMELEN;
588: m_checkSum = (int) TarUtils.parseOctal(header, offset,
589: TarConstants.CHKSUMLEN);
590: offset += TarConstants.CHKSUMLEN;
591: m_linkFlag = header[offset++];
592: m_linkName = TarUtils.parseName(header, offset, NAMELEN);
593: offset += NAMELEN;
594: m_magic = TarUtils.parseName(header, offset,
595: TarConstants.MAGICLEN);
596: offset += TarConstants.MAGICLEN;
597: m_userName = TarUtils.parseName(header, offset,
598: TarConstants.UNAMELEN);
599: offset += TarConstants.UNAMELEN;
600: m_groupName = TarUtils.parseName(header, offset,
601: TarConstants.GNAMELEN);
602: offset += TarConstants.GNAMELEN;
603: m_devMajor = (int) TarUtils.parseOctal(header, offset,
604: TarConstants.DEVLEN);
605: offset += TarConstants.DEVLEN;
606: m_devMinor = (int) TarUtils.parseOctal(header, offset,
607: TarConstants.DEVLEN);
608: }
609:
610: /**
611: * Write an entry's header information to a header buffer.
612: *
613: * @param buffer The tar entry header buffer to fill in.
614: */
615: public void writeEntryHeader(final byte[] buffer) {
616: int offset = 0;
617:
618: offset = TarUtils.getNameBytes(m_name, buffer, offset, NAMELEN);
619: offset = TarUtils.getOctalBytes(m_mode, buffer, offset,
620: TarConstants.MODELEN);
621: offset = TarUtils.getOctalBytes(m_userID, buffer, offset,
622: TarConstants.UIDLEN);
623: offset = TarUtils.getOctalBytes(m_groupID, buffer, offset,
624: TarConstants.GIDLEN);
625: offset = TarUtils.getLongOctalBytes(m_size, buffer, offset,
626: TarConstants.SIZELEN);
627: offset = TarUtils.getLongOctalBytes(m_modTime, buffer, offset,
628: TarConstants.MODTIMELEN);
629:
630: final int checkSumOffset = offset;
631: for (int i = 0; i < TarConstants.CHKSUMLEN; ++i) {
632: buffer[offset++] = (byte) ' ';
633: }
634:
635: buffer[offset++] = m_linkFlag;
636: offset = TarUtils.getNameBytes(m_linkName, buffer, offset,
637: NAMELEN);
638: offset = TarUtils.getNameBytes(m_magic, buffer, offset,
639: TarConstants.MAGICLEN);
640: offset = TarUtils.getNameBytes(m_userName, buffer, offset,
641: TarConstants.UNAMELEN);
642: offset = TarUtils.getNameBytes(m_groupName, buffer, offset,
643: TarConstants.GNAMELEN);
644: offset = TarUtils.getOctalBytes(m_devMajor, buffer, offset,
645: TarConstants.DEVLEN);
646: offset = TarUtils.getOctalBytes(m_devMinor, buffer, offset,
647: TarConstants.DEVLEN);
648:
649: while (offset < buffer.length) {
650: buffer[offset++] = 0;
651: }
652:
653: final long checkSum = TarUtils.computeCheckSum(buffer);
654: TarUtils.getCheckSumOctalBytes(checkSum, buffer,
655: checkSumOffset, TarConstants.CHKSUMLEN);
656: }
657: }
|