1 package org.codehaus.plexus.components.io.attributes;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import javax.annotation.Nonnull;
20 import javax.annotation.Nullable;
21
22 import java.io.File;
23 import java.io.IOException;
24 import java.nio.file.FileSystem;
25 import java.nio.file.Files;
26 import java.nio.file.LinkOption;
27 import java.nio.file.Path;
28 import java.nio.file.attribute.FileTime;
29 import java.nio.file.attribute.PosixFilePermission;
30 import java.security.Principal;
31 import java.util.Collections;
32 import java.util.Map;
33 import java.util.Set;
34 import java.util.WeakHashMap;
35 import java.util.concurrent.ConcurrentHashMap;
36
37
38
39
40
41 public class FileAttributes implements PlexusIoResourceAttributes {
42 public static final LinkOption[] FOLLOW_LINK_OPTIONS = new LinkOption[] {};
43
44 public static final LinkOption[] NOFOLLOW_LINK_OPTIONS = new LinkOption[] {LinkOption.NOFOLLOW_LINKS};
45
46 @Nullable
47 private final Integer groupId;
48
49 @Nullable
50 private final String groupName;
51
52 @Nullable
53 private final Integer userId;
54
55 private final String userName;
56
57 private final boolean symbolicLink;
58
59 private final boolean regularFile;
60
61 private final boolean directory;
62
63 private final boolean other;
64
65 private final int octalMode;
66
67 private final Set<PosixFilePermission> permissions;
68
69 private final long size;
70
71 private final FileTime lastModifiedTime;
72
73 private static final Map<FileSystem, Map<Integer, String>> UIDS_CACHE =
74 Collections.synchronizedMap(new WeakHashMap<>());
75
76 private static final Map<FileSystem, Map<Integer, String>> GIDS_CACHE =
77 Collections.synchronizedMap(new WeakHashMap<>());
78
79
80
81
82 @Deprecated
83 public FileAttributes(
84 @Nonnull File file, @Nonnull Map<Integer, String> userCache, @Nonnull Map<Integer, String> groupCache)
85 throws IOException {
86 this(file);
87 }
88
89 public FileAttributes(@Nonnull File file) throws IOException {
90 this(file.toPath(), false);
91 }
92
93 public FileAttributes(@Nonnull File file, boolean followLinks) throws IOException {
94 this(file.toPath(), followLinks);
95 }
96
97 private static Map<Integer, String> getUserCache(FileSystem fs) {
98 return UIDS_CACHE.computeIfAbsent(fs, f -> new ConcurrentHashMap<>());
99 }
100
101 private static Map<Integer, String> getGroupCache(FileSystem fs) {
102 return GIDS_CACHE.computeIfAbsent(fs, f -> new ConcurrentHashMap<>());
103 }
104
105 public FileAttributes(@Nonnull Path path, boolean followLinks) throws IOException {
106 LinkOption[] options = followLinks ? FOLLOW_LINK_OPTIONS : NOFOLLOW_LINK_OPTIONS;
107 Set<String> views = path.getFileSystem().supportedFileAttributeViews();
108 String names;
109 if (views.contains("unix")) {
110 names =
111 "unix:gid,uid,isSymbolicLink,isRegularFile,isDirectory,isOther,mode,permissions,size,lastModifiedTime";
112 } else if (views.contains("posix")) {
113 names = "posix:*";
114 } else {
115 names = "basic:*";
116 }
117 Map<String, Object> attrs = Files.readAttributes(path, names, options);
118 this.groupId = (Integer) attrs.get("gid");
119 if (attrs.containsKey("group")) {
120 this.groupName = ((Principal) attrs.get("group")).getName();
121 } else if (this.groupId != null) {
122 Map<Integer, String> cache = getGroupCache(path.getFileSystem());
123 String name = cache.get(this.groupId);
124 if (name == null) {
125 name = getPrincipalName(path, "unix:group");
126 cache.put(this.groupId, name);
127 }
128 this.groupName = name;
129 } else {
130 this.groupName = null;
131 }
132 this.userId = (Integer) attrs.get("uid");
133 if (attrs.containsKey("owner")) {
134 this.userName = ((Principal) attrs.get("owner")).getName();
135 } else if (this.userId != null) {
136 Map<Integer, String> cache = getUserCache(path.getFileSystem());
137 String name = cache.get(this.userId);
138 if (name == null) {
139 name = getPrincipalName(path, "unix:owner");
140 cache.put(this.userId, name);
141 }
142 this.userName = name;
143 } else if (views.contains("owner")) {
144 this.userName = getPrincipalName(path, "owner:owner");
145 } else {
146 this.userName = null;
147 }
148 this.symbolicLink = (Boolean) attrs.get("isSymbolicLink");
149 this.regularFile = (Boolean) attrs.get("isRegularFile");
150 this.directory = (Boolean) attrs.get("isDirectory");
151 this.other = (Boolean) attrs.get("isOther");
152 this.octalMode = attrs.containsKey("mode")
153 ? (Integer) attrs.get("mode") & 0xfff
154 : PlexusIoResourceAttributes.UNKNOWN_OCTAL_MODE;
155
156 this.permissions = attrs.containsKey("permissions")
157 ? (Set<PosixFilePermission>) attrs.get("permissions")
158 : Collections.emptySet();
159 this.size = (Long) attrs.get("size");
160 this.lastModifiedTime = (FileTime) attrs.get("lastModifiedTime");
161 }
162
163 private static String getPrincipalName(Path path, String attribute) throws IOException {
164 Object owner = Files.getAttribute(path, attribute, LinkOption.NOFOLLOW_LINKS);
165 return ((Principal) owner).getName();
166 }
167
168 public FileAttributes(
169 @Nullable Integer userId,
170 String userName,
171 @Nullable Integer groupId,
172 @Nullable String groupName,
173 int octalMode,
174 boolean symbolicLink,
175 boolean regularFile,
176 boolean directory,
177 boolean other,
178 Set<PosixFilePermission> permissions,
179 long size,
180 FileTime lastModifiedTime) {
181 this.userId = userId;
182 this.userName = userName;
183 this.groupId = groupId;
184 this.groupName = groupName;
185 this.octalMode = octalMode;
186 this.symbolicLink = symbolicLink;
187 this.regularFile = regularFile;
188 this.directory = directory;
189 this.other = other;
190 this.permissions = permissions;
191 this.size = size;
192 this.lastModifiedTime = lastModifiedTime;
193 }
194
195 public static @Nonnull PlexusIoResourceAttributes uncached(@Nonnull File file) throws IOException {
196 return new FileAttributes(file);
197 }
198
199 @Nullable
200 public Integer getGroupId() {
201
202 return groupId;
203 }
204
205 public boolean hasGroupId() {
206 return false;
207 }
208
209 public boolean hasUserId() {
210 return false;
211 }
212
213 @Nullable
214 public String getGroupName() {
215 return groupName;
216 }
217
218 public Integer getUserId() {
219 return userId;
220 }
221
222 public String getUserName() {
223 return userName;
224 }
225
226 public boolean isGroupExecutable() {
227 return containsPermission(PosixFilePermission.GROUP_EXECUTE);
228 }
229
230 private boolean containsPermission(PosixFilePermission groupExecute) {
231 return permissions.contains(groupExecute);
232 }
233
234 public boolean isGroupReadable() {
235 return containsPermission(PosixFilePermission.GROUP_READ);
236 }
237
238 public boolean isGroupWritable() {
239 return containsPermission(PosixFilePermission.GROUP_WRITE);
240 }
241
242 public boolean isOwnerExecutable() {
243 return containsPermission(PosixFilePermission.OWNER_EXECUTE);
244 }
245
246 public boolean isOwnerReadable() {
247 return containsPermission(PosixFilePermission.OWNER_READ);
248 }
249
250 public boolean isOwnerWritable() {
251 return containsPermission(PosixFilePermission.OWNER_WRITE);
252 }
253
254 public boolean isWorldExecutable() {
255 return containsPermission(PosixFilePermission.OTHERS_EXECUTE);
256 }
257
258 public boolean isWorldReadable() {
259 return containsPermission(PosixFilePermission.OTHERS_READ);
260 }
261
262 public boolean isWorldWritable() {
263 return containsPermission(PosixFilePermission.OTHERS_WRITE);
264 }
265
266 public String toString() {
267 StringBuilder sb = new StringBuilder();
268 sb.append(System.lineSeparator());
269 sb.append("File Attributes:");
270 sb.append(System.lineSeparator());
271 sb.append("------------------------------");
272 sb.append(System.lineSeparator());
273 sb.append("user: ");
274 sb.append(userName == null ? "" : userName);
275 sb.append(System.lineSeparator());
276 sb.append("group: ");
277 sb.append(groupName == null ? "" : groupName);
278 sb.append(System.lineSeparator());
279 sb.append("uid: ");
280 sb.append(hasUserId() ? Integer.toString(userId) : "");
281 sb.append(System.lineSeparator());
282 sb.append("gid: ");
283 sb.append(hasGroupId() ? Integer.toString(groupId) : "");
284
285 return sb.toString();
286 }
287
288 public int getOctalMode() {
289 return octalMode;
290 }
291
292 public int calculatePosixOctalMode() {
293 int result = 0;
294
295 if (isOwnerReadable()) {
296 result |= AttributeConstants.OCTAL_OWNER_READ;
297 }
298
299 if (isOwnerWritable()) {
300 result |= AttributeConstants.OCTAL_OWNER_WRITE;
301 }
302
303 if (isOwnerExecutable()) {
304 result |= AttributeConstants.OCTAL_OWNER_EXECUTE;
305 }
306
307 if (isGroupReadable()) {
308 result |= AttributeConstants.OCTAL_GROUP_READ;
309 }
310
311 if (isGroupWritable()) {
312 result |= AttributeConstants.OCTAL_GROUP_WRITE;
313 }
314
315 if (isGroupExecutable()) {
316 result |= AttributeConstants.OCTAL_GROUP_EXECUTE;
317 }
318
319 if (isWorldReadable()) {
320 result |= AttributeConstants.OCTAL_WORLD_READ;
321 }
322
323 if (isWorldWritable()) {
324 result |= AttributeConstants.OCTAL_WORLD_WRITE;
325 }
326
327 if (isWorldExecutable()) {
328 result |= AttributeConstants.OCTAL_WORLD_EXECUTE;
329 }
330
331 return result;
332 }
333
334 public String getOctalModeString() {
335 return Integer.toString(getOctalMode(), 8);
336 }
337
338 public boolean isSymbolicLink() {
339 return symbolicLink;
340 }
341
342 public boolean isRegularFile() {
343 return regularFile;
344 }
345
346 public boolean isDirectory() {
347 return directory;
348 }
349
350 public boolean isOther() {
351 return other;
352 }
353
354 public long getSize() {
355 return size;
356 }
357
358 public FileTime getLastModifiedTime() {
359 return lastModifiedTime;
360 }
361
362 protected Set<PosixFilePermission> getPermissions() {
363 return permissions;
364 }
365 }