1/*
2 * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.nio.fs;
27
28import java.nio.file.attribute.*;
29import java.util.concurrent.TimeUnit;
30import java.util.Set;
31import java.util.HashSet;
32
33/**
34 * Unix implementation of PosixFileAttributes.
35 */
36
37class UnixFileAttributes
38    implements PosixFileAttributes
39{
40    private int     st_mode;
41    private long    st_ino;
42    private long    st_dev;
43    private long    st_rdev;
44    private int     st_nlink;
45    private int     st_uid;
46    private int     st_gid;
47    private long    st_size;
48    private long    st_atime_sec;
49    private long    st_atime_nsec;
50    private long    st_mtime_sec;
51    private long    st_mtime_nsec;
52    private long    st_ctime_sec;
53    private long    st_ctime_nsec;
54    private long    st_birthtime_sec;
55
56    // created lazily
57    private volatile UserPrincipal owner;
58    private volatile GroupPrincipal group;
59    private volatile UnixFileKey key;
60
61    private UnixFileAttributes() {
62    }
63
64    // get the UnixFileAttributes for a given file
65    static UnixFileAttributes get(UnixPath path, boolean followLinks)
66        throws UnixException
67    {
68        UnixFileAttributes attrs = new UnixFileAttributes();
69        if (followLinks) {
70            UnixNativeDispatcher.stat(path, attrs);
71        } else {
72            UnixNativeDispatcher.lstat(path, attrs);
73        }
74        return attrs;
75    }
76
77    // get the UnixFileAttributes for an open file
78    static UnixFileAttributes get(int fd) throws UnixException {
79        UnixFileAttributes attrs = new UnixFileAttributes();
80        UnixNativeDispatcher.fstat(fd, attrs);
81        return attrs;
82    }
83
84    // get the UnixFileAttributes for a given file, relative to open directory
85    static UnixFileAttributes get(int dfd, UnixPath path, boolean followLinks)
86        throws UnixException
87    {
88        UnixFileAttributes attrs = new UnixFileAttributes();
89        int flag = (followLinks) ? 0 : UnixConstants.AT_SYMLINK_NOFOLLOW;
90        UnixNativeDispatcher.fstatat(dfd, path.asByteArray(), flag, attrs);
91        return attrs;
92    }
93
94    // package-private
95    boolean isSameFile(UnixFileAttributes attrs) {
96        return ((st_ino == attrs.st_ino) && (st_dev == attrs.st_dev));
97    }
98
99    // package-private
100    int mode()  { return st_mode; }
101    long ino()  { return st_ino; }
102    long dev()  { return st_dev; }
103    long rdev() { return st_rdev; }
104    int nlink() { return st_nlink; }
105    int uid()   { return st_uid; }
106    int gid()   { return st_gid; }
107
108    private static FileTime toFileTime(long sec, long nsec) {
109        if (nsec == 0) {
110            return FileTime.from(sec, TimeUnit.SECONDS);
111        } else {
112            // truncate to microseconds to avoid overflow with timestamps
113            // way out into the future. We can re-visit this if FileTime
114            // is updated to define a from(secs,nsecs) method.
115            long micro = sec*1000000L + nsec/1000L;
116            return FileTime.from(micro, TimeUnit.MICROSECONDS);
117        }
118    }
119
120    FileTime ctime() {
121        return toFileTime(st_ctime_sec, st_ctime_nsec);
122    }
123
124    boolean isDevice() {
125        int type = st_mode & UnixConstants.S_IFMT;
126        return (type == UnixConstants.S_IFCHR ||
127                type == UnixConstants.S_IFBLK  ||
128                type == UnixConstants.S_IFIFO);
129    }
130
131    @Override
132    public FileTime lastModifiedTime() {
133        return toFileTime(st_mtime_sec, st_mtime_nsec);
134    }
135
136    @Override
137    public FileTime lastAccessTime() {
138        return toFileTime(st_atime_sec, st_atime_nsec);
139    }
140
141    @Override
142    public FileTime creationTime() {
143        if (UnixNativeDispatcher.birthtimeSupported()) {
144            return FileTime.from(st_birthtime_sec, TimeUnit.SECONDS);
145        } else {
146            // return last modified when birth time not supported
147            return lastModifiedTime();
148        }
149    }
150
151    @Override
152    public boolean isRegularFile() {
153       return ((st_mode & UnixConstants.S_IFMT) == UnixConstants.S_IFREG);
154    }
155
156    @Override
157    public boolean isDirectory() {
158        return ((st_mode & UnixConstants.S_IFMT) == UnixConstants.S_IFDIR);
159    }
160
161    @Override
162    public boolean isSymbolicLink() {
163        return ((st_mode & UnixConstants.S_IFMT) == UnixConstants.S_IFLNK);
164    }
165
166    @Override
167    public boolean isOther() {
168        int type = st_mode & UnixConstants.S_IFMT;
169        return (type != UnixConstants.S_IFREG &&
170                type != UnixConstants.S_IFDIR &&
171                type != UnixConstants.S_IFLNK);
172    }
173
174    @Override
175    public long size() {
176        return st_size;
177    }
178
179    @Override
180    public UnixFileKey fileKey() {
181        if (key == null) {
182            synchronized (this) {
183                if (key == null) {
184                    key = new UnixFileKey(st_dev, st_ino);
185                }
186            }
187        }
188        return key;
189    }
190
191    @Override
192    public UserPrincipal owner() {
193        if (owner == null) {
194            synchronized (this) {
195                if (owner == null) {
196                    owner = UnixUserPrincipals.fromUid(st_uid);
197                }
198            }
199        }
200        return owner;
201    }
202
203    @Override
204    public GroupPrincipal group() {
205        if (group == null) {
206            synchronized (this) {
207                if (group == null) {
208                    group = UnixUserPrincipals.fromGid(st_gid);
209                }
210            }
211        }
212        return group;
213    }
214
215    @Override
216    public Set<PosixFilePermission> permissions() {
217        int bits = (st_mode & UnixConstants.S_IAMB);
218        HashSet<PosixFilePermission> perms = new HashSet<>();
219
220        if ((bits & UnixConstants.S_IRUSR) > 0)
221            perms.add(PosixFilePermission.OWNER_READ);
222        if ((bits & UnixConstants.S_IWUSR) > 0)
223            perms.add(PosixFilePermission.OWNER_WRITE);
224        if ((bits & UnixConstants.S_IXUSR) > 0)
225            perms.add(PosixFilePermission.OWNER_EXECUTE);
226
227        if ((bits & UnixConstants.S_IRGRP) > 0)
228            perms.add(PosixFilePermission.GROUP_READ);
229        if ((bits & UnixConstants.S_IWGRP) > 0)
230            perms.add(PosixFilePermission.GROUP_WRITE);
231        if ((bits & UnixConstants.S_IXGRP) > 0)
232            perms.add(PosixFilePermission.GROUP_EXECUTE);
233
234        if ((bits & UnixConstants.S_IROTH) > 0)
235            perms.add(PosixFilePermission.OTHERS_READ);
236        if ((bits & UnixConstants.S_IWOTH) > 0)
237            perms.add(PosixFilePermission.OTHERS_WRITE);
238        if ((bits & UnixConstants.S_IXOTH) > 0)
239            perms.add(PosixFilePermission.OTHERS_EXECUTE);
240
241        return perms;
242    }
243
244    // wrap this object with BasicFileAttributes object to prevent leaking of
245    // user information
246    BasicFileAttributes asBasicFileAttributes() {
247        return UnixAsBasicFileAttributes.wrap(this);
248    }
249
250    // unwrap BasicFileAttributes to get the underlying UnixFileAttributes
251    // object. Returns null is not wrapped.
252    static UnixFileAttributes toUnixFileAttributes(BasicFileAttributes attrs) {
253        if (attrs instanceof UnixFileAttributes)
254            return (UnixFileAttributes)attrs;
255        if (attrs instanceof UnixAsBasicFileAttributes) {
256            return ((UnixAsBasicFileAttributes)attrs).unwrap();
257        }
258        return null;
259    }
260
261    // wrap a UnixFileAttributes object as a BasicFileAttributes
262    private static class UnixAsBasicFileAttributes implements BasicFileAttributes {
263        private final UnixFileAttributes attrs;
264
265        private UnixAsBasicFileAttributes(UnixFileAttributes attrs) {
266            this.attrs = attrs;
267        }
268
269        static UnixAsBasicFileAttributes wrap(UnixFileAttributes attrs) {
270            return new UnixAsBasicFileAttributes(attrs);
271        }
272
273        UnixFileAttributes unwrap() {
274            return attrs;
275        }
276
277        @Override
278        public FileTime lastModifiedTime() {
279            return attrs.lastModifiedTime();
280        }
281        @Override
282        public FileTime lastAccessTime() {
283            return attrs.lastAccessTime();
284        }
285        @Override
286        public FileTime creationTime() {
287            return attrs.creationTime();
288        }
289        @Override
290        public boolean isRegularFile() {
291            return attrs.isRegularFile();
292        }
293        @Override
294        public boolean isDirectory() {
295            return attrs.isDirectory();
296        }
297        @Override
298        public boolean isSymbolicLink() {
299            return attrs.isSymbolicLink();
300        }
301        @Override
302        public boolean isOther() {
303            return attrs.isOther();
304        }
305        @Override
306        public long size() {
307            return attrs.size();
308        }
309        @Override
310        public Object fileKey() {
311            return attrs.fileKey();
312        }
313    }
314}
315