001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.compress.archivers.ar; 020 021import java.io.File; 022import java.io.IOException; 023import java.nio.file.Files; 024import java.nio.file.LinkOption; 025import java.nio.file.Path; 026import java.util.Date; 027import java.util.Objects; 028 029import org.apache.commons.compress.archivers.ArchiveEntry; 030 031/** 032 * Represents an archive entry in the "ar" format. 033 * <p> 034 * Each AR archive starts with "!<arch>" followed by a LF. After these 8 bytes 035 * the archive entries are listed. The format of an entry header is as it follows: 036 * </p> 037 * <pre> 038 * START BYTE END BYTE NAME FORMAT LENGTH 039 * 0 15 File name ASCII 16 040 * 16 27 Modification timestamp Decimal 12 041 * 28 33 Owner ID Decimal 6 042 * 34 39 Group ID Decimal 6 043 * 40 47 File mode Octal 8 044 * 48 57 File size (bytes) Decimal 10 045 * 58 59 File magic \140\012 2 046 * </pre> 047 * <p> 048 * This specifies that an ar archive entry header contains 60 bytes. 049 * </p> 050 * <p> 051 * Due to the limitation of the file name length to 16 bytes GNU and 052 * BSD has their own variants of this format. Currently Commons 053 * Compress can read but not write the GNU variant. It fully supports 054 * the BSD variant. 055 * </p> 056 * 057 * @see <a href="https://www.freebsd.org/cgi/man.cgi?query=ar&sektion=5">ar man page</a> 058 * @Immutable 059 */ 060public class ArArchiveEntry implements ArchiveEntry { 061 062 /** The header for each entry */ 063 public static final String HEADER = "!<arch>\n"; 064 065 /** The trailer for each entry */ 066 public static final String TRAILER = "`\012"; 067 068 private static final int DEFAULT_MODE = 33188; // = (octal) 0100644 069 070 /** 071 * SVR4/GNU adds a trailing / to names; BSD does not. 072 * They also vary in how names longer than 16 characters are represented. 073 * (Not yet fully supported by this implementation) 074 */ 075 private final String name; 076 private final int userId; 077 private final int groupId; 078 private final int mode; 079 private final long lastModified; 080 private final long length; 081 082 /** 083 * Creates a new instance using the attributes of the given file 084 * @param inputFile the file to create an entry from 085 * @param entryName the name of the entry 086 */ 087 public ArArchiveEntry(final File inputFile, final String entryName) { 088 // TODO sort out mode 089 this(entryName, inputFile.isFile() ? inputFile.length() : 0, 090 0, 0, DEFAULT_MODE, inputFile.lastModified() / 1000); 091 } 092 093 /** 094 * Creates a new instance using the attributes of the given file 095 * @param inputPath the file to create an entry from 096 * @param entryName the name of the entry 097 * @param options options indicating how symbolic links are handled. 098 * @throws IOException if an I/O error occurs. 099 * @since 1.21 100 */ 101 public ArArchiveEntry(final Path inputPath, final String entryName, final LinkOption... options) throws IOException { 102 this(entryName, Files.isRegularFile(inputPath, options) ? Files.size(inputPath) : 0, 0, 0, DEFAULT_MODE, 103 Files.getLastModifiedTime(inputPath, options).toMillis() / 1000); 104 } 105 106 /** 107 * Create a new instance using a couple of default values. 108 * 109 * <p>Sets userId and groupId to 0, the octal file mode to 644 and 110 * the last modified time to the current time.</p> 111 * 112 * @param name name of the entry 113 * @param length length of the entry in bytes 114 */ 115 public ArArchiveEntry(final String name, final long length) { 116 this(name, length, 0, 0, DEFAULT_MODE, 117 System.currentTimeMillis() / 1000); 118 } 119 120 /** 121 * Create a new instance. 122 * 123 * @param name name of the entry 124 * @param length length of the entry in bytes 125 * @param userId numeric user id 126 * @param groupId numeric group id 127 * @param mode file mode 128 * @param lastModified last modified time in seconds since the epoch 129 */ 130 public ArArchiveEntry(final String name, final long length, final int userId, final int groupId, 131 final int mode, final long lastModified) { 132 this.name = name; 133 if (length < 0) { 134 throw new IllegalArgumentException("length must not be negative"); 135 } 136 this.length = length; 137 this.userId = userId; 138 this.groupId = groupId; 139 this.mode = mode; 140 this.lastModified = lastModified; 141 } 142 143 @Override 144 public boolean equals(final Object obj) { 145 if (this == obj) { 146 return true; 147 } 148 if (obj == null || getClass() != obj.getClass()) { 149 return false; 150 } 151 final ArArchiveEntry other = (ArArchiveEntry) obj; 152 if (name == null) { 153 return other.name == null; 154 } 155 return name.equals(other.name); 156 } 157 158 /** 159 * Gets the group ID. 160 * 161 * @return the group ID. 162 */ 163 public int getGroupId() { 164 return groupId; 165 } 166 167 /** 168 * Gets the last modified time in seconds since the epoch. 169 * 170 * @return the last modified date. 171 */ 172 public long getLastModified() { 173 return lastModified; 174 } 175 176 @Override 177 public Date getLastModifiedDate() { 178 return new Date(1000 * getLastModified()); 179 } 180 181 /** 182 * Gets the length. 183 * 184 * @return the length. 185 */ 186 public long getLength() { 187 return length; 188 } 189 190 /** 191 * Gets the mode. 192 * 193 * @return the mode. 194 */ 195 public int getMode() { 196 return mode; 197 } 198 199 @Override 200 public String getName() { 201 return name; 202 } 203 204 @Override 205 public long getSize() { 206 return this.getLength(); 207 } 208 209 /** 210 * Gets the user ID. 211 * 212 * @return the user ID. 213 */ 214 public int getUserId() { 215 return userId; 216 } 217 218 @Override 219 public int hashCode() { 220 return Objects.hash(name); 221 } 222 223 @Override 224 public boolean isDirectory() { 225 return false; 226 } 227}