1/*
2 * Copyright (c) 2008, 2015, 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.*;
29import java.nio.file.attribute.*;
30import java.util.*;
31import java.util.concurrent.TimeUnit;
32import java.io.IOException;
33
34import static sun.nio.fs.UnixNativeDispatcher.*;
35
36class UnixFileAttributeViews {
37
38    static class Basic extends AbstractBasicFileAttributeView {
39        protected final UnixPath file;
40        protected final boolean followLinks;
41
42        Basic(UnixPath file, boolean followLinks) {
43            this.file = file;
44            this.followLinks = followLinks;
45        }
46
47        @Override
48        public BasicFileAttributes readAttributes() throws IOException {
49            file.checkRead();
50            try {
51                 UnixFileAttributes attrs =
52                     UnixFileAttributes.get(file, followLinks);
53                 return attrs.asBasicFileAttributes();
54            } catch (UnixException x) {
55                x.rethrowAsIOException(file);
56                return null;    // keep compiler happy
57            }
58        }
59
60        @Override
61        public void setTimes(FileTime lastModifiedTime,
62                             FileTime lastAccessTime,
63                             FileTime createTime) throws IOException
64        {
65            // null => don't change
66            if (lastModifiedTime == null && lastAccessTime == null) {
67                // no effect
68                return;
69            }
70
71            // permission check
72            file.checkWrite();
73
74            boolean haveFd = false;
75            boolean useFutimes = false;
76            int fd = -1;
77            try {
78                fd = file.openForAttributeAccess(followLinks);
79                if (fd != -1) {
80                    haveFd = true;
81                    useFutimes = futimesSupported();
82                }
83            } catch (UnixException x) {
84                if (x.errno() != UnixConstants.ENXIO) {
85                    x.rethrowAsIOException(file);
86                }
87            }
88
89            try {
90                // assert followLinks || !UnixFileAttributes.get(fd).isSymbolicLink();
91
92                // if not changing both attributes then need existing attributes
93                if (lastModifiedTime == null || lastAccessTime == null) {
94                    try {
95                        UnixFileAttributes attrs = haveFd ?
96                            UnixFileAttributes.get(fd) :
97                            UnixFileAttributes.get(file, followLinks);
98                        if (lastModifiedTime == null)
99                            lastModifiedTime = attrs.lastModifiedTime();
100                        if (lastAccessTime == null)
101                            lastAccessTime = attrs.lastAccessTime();
102                    } catch (UnixException x) {
103                        x.rethrowAsIOException(file);
104                    }
105                }
106
107                // uptime times
108                long modValue = lastModifiedTime.to(TimeUnit.MICROSECONDS);
109                long accessValue= lastAccessTime.to(TimeUnit.MICROSECONDS);
110
111                boolean retry = false;
112                try {
113                    if (useFutimes) {
114                        futimes(fd, accessValue, modValue);
115                    } else {
116                        utimes(file, accessValue, modValue);
117                    }
118                } catch (UnixException x) {
119                    // if futimes/utimes fails with EINVAL and one/both of the times is
120                    // negative then we adjust the value to the epoch and retry.
121                    if (x.errno() == UnixConstants.EINVAL &&
122                        (modValue < 0L || accessValue < 0L)) {
123                        retry = true;
124                    } else {
125                        x.rethrowAsIOException(file);
126                    }
127                }
128                if (retry) {
129                    if (modValue < 0L) modValue = 0L;
130                    if (accessValue < 0L) accessValue= 0L;
131                    try {
132                        if (useFutimes) {
133                            futimes(fd, accessValue, modValue);
134                        } else {
135                            utimes(file, accessValue, modValue);
136                        }
137                    } catch (UnixException x) {
138                        x.rethrowAsIOException(file);
139                    }
140                }
141            } finally {
142                close(fd);
143            }
144        }
145    }
146
147    private static class Posix extends Basic implements PosixFileAttributeView {
148        private static final String PERMISSIONS_NAME = "permissions";
149        private static final String OWNER_NAME = "owner";
150        private static final String GROUP_NAME = "group";
151
152        // the names of the posix attributes (includes basic)
153        static final Set<String> posixAttributeNames =
154            Util.newSet(basicAttributeNames, PERMISSIONS_NAME, OWNER_NAME, GROUP_NAME);
155
156        Posix(UnixPath file, boolean followLinks) {
157            super(file, followLinks);
158        }
159
160        final void checkReadExtended() {
161            SecurityManager sm = System.getSecurityManager();
162            if (sm != null) {
163                file.checkRead();
164                sm.checkPermission(new RuntimePermission("accessUserInformation"));
165            }
166        }
167
168        final void checkWriteExtended() {
169            SecurityManager sm = System.getSecurityManager();
170            if (sm != null) {
171                file.checkWrite();
172                sm.checkPermission(new RuntimePermission("accessUserInformation"));
173            }
174        }
175
176        @Override
177        public String name() {
178            return "posix";
179        }
180
181        @Override
182        @SuppressWarnings("unchecked")
183        public void setAttribute(String attribute, Object value)
184            throws IOException
185        {
186            if (attribute.equals(PERMISSIONS_NAME)) {
187                setPermissions((Set<PosixFilePermission>)value);
188                return;
189            }
190            if (attribute.equals(OWNER_NAME)) {
191                setOwner((UserPrincipal)value);
192                return;
193            }
194            if (attribute.equals(GROUP_NAME)) {
195                setGroup((GroupPrincipal)value);
196                return;
197            }
198            super.setAttribute(attribute, value);
199        }
200
201        /**
202         * Invoked by readAttributes or sub-classes to add all matching posix
203         * attributes to the builder
204         */
205        final void addRequestedPosixAttributes(PosixFileAttributes attrs,
206                                               AttributesBuilder builder)
207        {
208            addRequestedBasicAttributes(attrs, builder);
209            if (builder.match(PERMISSIONS_NAME))
210                builder.add(PERMISSIONS_NAME, attrs.permissions());
211            if (builder.match(OWNER_NAME))
212                 builder.add(OWNER_NAME, attrs.owner());
213            if (builder.match(GROUP_NAME))
214                builder.add(GROUP_NAME, attrs.group());
215        }
216
217        @Override
218        public Map<String,Object> readAttributes(String[] requested)
219            throws IOException
220        {
221            AttributesBuilder builder =
222                AttributesBuilder.create(posixAttributeNames, requested);
223            PosixFileAttributes attrs = readAttributes();
224            addRequestedPosixAttributes(attrs, builder);
225            return builder.unmodifiableMap();
226        }
227
228        @Override
229        public UnixFileAttributes readAttributes() throws IOException {
230            checkReadExtended();
231            try {
232                 return UnixFileAttributes.get(file, followLinks);
233            } catch (UnixException x) {
234                x.rethrowAsIOException(file);
235                return null;    // keep compiler happy
236            }
237        }
238
239        // chmod
240        final void setMode(int mode) throws IOException {
241            checkWriteExtended();
242            try {
243                if (followLinks) {
244                    chmod(file, mode);
245                } else {
246                    int fd = file.openForAttributeAccess(false);
247                    try {
248                        fchmod(fd, mode);
249                    } finally {
250                        close(fd);
251                    }
252                }
253            } catch (UnixException x) {
254                x.rethrowAsIOException(file);
255            }
256        }
257
258        // chown
259        final void setOwners(int uid, int gid) throws IOException {
260            checkWriteExtended();
261            try {
262                if (followLinks) {
263                    chown(file, uid, gid);
264                } else {
265                    lchown(file, uid, gid);
266                }
267            } catch (UnixException x) {
268                x.rethrowAsIOException(file);
269            }
270        }
271
272        @Override
273        public void setPermissions(Set<PosixFilePermission> perms)
274            throws IOException
275        {
276            setMode(UnixFileModeAttribute.toUnixMode(perms));
277        }
278
279        @Override
280        public void setOwner(UserPrincipal owner)
281            throws IOException
282        {
283            if (owner == null)
284                throw new NullPointerException("'owner' is null");
285            if (!(owner instanceof UnixUserPrincipals.User))
286                throw new ProviderMismatchException();
287            if (owner instanceof UnixUserPrincipals.Group)
288                throw new IOException("'owner' parameter can't be a group");
289            int uid = ((UnixUserPrincipals.User)owner).uid();
290            setOwners(uid, -1);
291        }
292
293        @Override
294        public UserPrincipal getOwner() throws IOException {
295            return readAttributes().owner();
296        }
297
298        @Override
299        public void setGroup(GroupPrincipal group)
300            throws IOException
301        {
302            if (group == null)
303                throw new NullPointerException("'owner' is null");
304            if (!(group instanceof UnixUserPrincipals.Group))
305                throw new ProviderMismatchException();
306            int gid = ((UnixUserPrincipals.Group)group).gid();
307            setOwners(-1, gid);
308        }
309    }
310
311    private static class Unix extends Posix {
312        private static final String MODE_NAME = "mode";
313        private static final String INO_NAME = "ino";
314        private static final String DEV_NAME = "dev";
315        private static final String RDEV_NAME = "rdev";
316        private static final String NLINK_NAME = "nlink";
317        private static final String UID_NAME = "uid";
318        private static final String GID_NAME = "gid";
319        private static final String CTIME_NAME = "ctime";
320
321        // the names of the unix attributes (including posix)
322        static final Set<String> unixAttributeNames =
323            Util.newSet(posixAttributeNames,
324                        MODE_NAME, INO_NAME, DEV_NAME, RDEV_NAME,
325                        NLINK_NAME, UID_NAME, GID_NAME, CTIME_NAME);
326
327        Unix(UnixPath file, boolean followLinks) {
328            super(file, followLinks);
329        }
330
331        @Override
332        public String name() {
333            return "unix";
334        }
335
336        @Override
337        public void setAttribute(String attribute, Object value)
338            throws IOException
339        {
340            if (attribute.equals(MODE_NAME)) {
341                setMode((Integer)value);
342                return;
343            }
344            if (attribute.equals(UID_NAME)) {
345                setOwners((Integer)value, -1);
346                return;
347            }
348            if (attribute.equals(GID_NAME)) {
349                setOwners(-1, (Integer)value);
350                return;
351            }
352            super.setAttribute(attribute, value);
353        }
354
355        @Override
356        public Map<String,Object> readAttributes(String[] requested)
357            throws IOException
358        {
359            AttributesBuilder builder =
360                AttributesBuilder.create(unixAttributeNames, requested);
361            UnixFileAttributes attrs = readAttributes();
362            addRequestedPosixAttributes(attrs, builder);
363            if (builder.match(MODE_NAME))
364                builder.add(MODE_NAME, attrs.mode());
365            if (builder.match(INO_NAME))
366                builder.add(INO_NAME, attrs.ino());
367            if (builder.match(DEV_NAME))
368                builder.add(DEV_NAME, attrs.dev());
369            if (builder.match(RDEV_NAME))
370                builder.add(RDEV_NAME, attrs.rdev());
371            if (builder.match(NLINK_NAME))
372                builder.add(NLINK_NAME, attrs.nlink());
373            if (builder.match(UID_NAME))
374                builder.add(UID_NAME, attrs.uid());
375            if (builder.match(GID_NAME))
376                builder.add(GID_NAME, attrs.gid());
377            if (builder.match(CTIME_NAME))
378                builder.add(CTIME_NAME, attrs.ctime());
379            return builder.unmodifiableMap();
380        }
381    }
382
383    static Basic createBasicView(UnixPath file, boolean followLinks) {
384        return new Basic(file, followLinks);
385    }
386
387    static Posix createPosixView(UnixPath file, boolean followLinks) {
388        return new Posix(file, followLinks);
389    }
390
391    static Unix createUnixView(UnixPath file, boolean followLinks) {
392        return new Unix(file, followLinks);
393    }
394
395    static FileOwnerAttributeViewImpl createOwnerView(UnixPath file, boolean followLinks) {
396        return new FileOwnerAttributeViewImpl(createPosixView(file, followLinks));
397    }
398}
399