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 @Nullable
164 private static String getPrincipalName(Path path, String attribute) {
165 try {
166 Object owner = Files.getAttribute(path, attribute, LinkOption.NOFOLLOW_LINKS);
167 return ((Principal) owner).getName();
168 } catch (IOException e) {
169
170
171 return null;
172 }
173 }
174
175 public FileAttributes(
176 @Nullable Integer userId,
177 String userName,
178 @Nullable Integer groupId,
179 @Nullable String groupName,
180 int octalMode,
181 boolean symbolicLink,
182 boolean regularFile,
183 boolean directory,
184 boolean other,
185 Set<PosixFilePermission> permissions,
186 long size,
187 FileTime lastModifiedTime) {
188 this.userId = userId;
189 this.userName = userName;
190 this.groupId = groupId;
191 this.groupName = groupName;
192 this.octalMode = octalMode;
193 this.symbolicLink = symbolicLink;
194 this.regularFile = regularFile;
195 this.directory = directory;
196 this.other = other;
197 this.permissions = permissions;
198 this.size = size;
199 this.lastModifiedTime = lastModifiedTime;
200 }
201
202 public static @Nonnull PlexusIoResourceAttributes uncached(@Nonnull File file) throws IOException {
203 return new FileAttributes(file);
204 }
205
206 @Nullable
207 public Integer getGroupId() {
208
209 return groupId;
210 }
211
212 public boolean hasGroupId() {
213 return false;
214 }
215
216 public boolean hasUserId() {
217 return false;
218 }
219
220 @Nullable
221 public String getGroupName() {
222 return groupName;
223 }
224
225 public Integer getUserId() {
226 return userId;
227 }
228
229 public String getUserName() {
230 return userName;
231 }
232
233 public boolean isGroupExecutable() {
234 return containsPermission(PosixFilePermission.GROUP_EXECUTE);
235 }
236
237 private boolean containsPermission(PosixFilePermission groupExecute) {
238 return permissions.contains(groupExecute);
239 }
240
241 public boolean isGroupReadable() {
242 return containsPermission(PosixFilePermission.GROUP_READ);
243 }
244
245 public boolean isGroupWritable() {
246 return containsPermission(PosixFilePermission.GROUP_WRITE);
247 }
248
249 public boolean isOwnerExecutable() {
250 return containsPermission(PosixFilePermission.OWNER_EXECUTE);
251 }
252
253 public boolean isOwnerReadable() {
254 return containsPermission(PosixFilePermission.OWNER_READ);
255 }
256
257 public boolean isOwnerWritable() {
258 return containsPermission(PosixFilePermission.OWNER_WRITE);
259 }
260
261 public boolean isWorldExecutable() {
262 return containsPermission(PosixFilePermission.OTHERS_EXECUTE);
263 }
264
265 public boolean isWorldReadable() {
266 return containsPermission(PosixFilePermission.OTHERS_READ);
267 }
268
269 public boolean isWorldWritable() {
270 return containsPermission(PosixFilePermission.OTHERS_WRITE);
271 }
272
273 public String toString() {
274 StringBuilder sb = new StringBuilder();
275 sb.append(System.lineSeparator());
276 sb.append("File Attributes:");
277 sb.append(System.lineSeparator());
278 sb.append("------------------------------");
279 sb.append(System.lineSeparator());
280 sb.append("user: ");
281 sb.append(userName == null ? "" : userName);
282 sb.append(System.lineSeparator());
283 sb.append("group: ");
284 sb.append(groupName == null ? "" : groupName);
285 sb.append(System.lineSeparator());
286 sb.append("uid: ");
287 sb.append(hasUserId() ? Integer.toString(userId) : "");
288 sb.append(System.lineSeparator());
289 sb.append("gid: ");
290 sb.append(hasGroupId() ? Integer.toString(groupId) : "");
291
292 return sb.toString();
293 }
294
295 public int getOctalMode() {
296 return octalMode;
297 }
298
299 public int calculatePosixOctalMode() {
300 int result = 0;
301
302 if (isOwnerReadable()) {
303 result |= AttributeConstants.OCTAL_OWNER_READ;
304 }
305
306 if (isOwnerWritable()) {
307 result |= AttributeConstants.OCTAL_OWNER_WRITE;
308 }
309
310 if (isOwnerExecutable()) {
311 result |= AttributeConstants.OCTAL_OWNER_EXECUTE;
312 }
313
314 if (isGroupReadable()) {
315 result |= AttributeConstants.OCTAL_GROUP_READ;
316 }
317
318 if (isGroupWritable()) {
319 result |= AttributeConstants.OCTAL_GROUP_WRITE;
320 }
321
322 if (isGroupExecutable()) {
323 result |= AttributeConstants.OCTAL_GROUP_EXECUTE;
324 }
325
326 if (isWorldReadable()) {
327 result |= AttributeConstants.OCTAL_WORLD_READ;
328 }
329
330 if (isWorldWritable()) {
331 result |= AttributeConstants.OCTAL_WORLD_WRITE;
332 }
333
334 if (isWorldExecutable()) {
335 result |= AttributeConstants.OCTAL_WORLD_EXECUTE;
336 }
337
338 return result;
339 }
340
341 public String getOctalModeString() {
342 return Integer.toString(getOctalMode(), 8);
343 }
344
345 public boolean isSymbolicLink() {
346 return symbolicLink;
347 }
348
349 public boolean isRegularFile() {
350 return regularFile;
351 }
352
353 public boolean isDirectory() {
354 return directory;
355 }
356
357 public boolean isOther() {
358 return other;
359 }
360
361 public long getSize() {
362 return size;
363 }
364
365 public FileTime getLastModifiedTime() {
366 return lastModifiedTime;
367 }
368
369 protected Set<PosixFilePermission> getPermissions() {
370 return permissions;
371 }
372 }