View Javadoc
1   package org.codehaus.plexus.components.io.attributes;
2   
3   /*
4    * Copyright 2007 The Codehaus Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * 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, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
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   * File attributes
39   * Immutable
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       * @deprecated use {@link #FileAttributes(File)} and remove the unused userCache and groupCache parameters
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         //noinspection unchecked
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 }