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 
26 package sun.nio.fs;
27 
28 import java.nio.file.attribute.*;
29 import java.util.Map;
30 import java.util.Set;
31 import java.io.IOException;
32 import jdk.internal.misc.Unsafe;
33 
34 import static sun.nio.fs.UnixNativeDispatcher.*;
35 import static sun.nio.fs.UnixConstants.*;
36 
37 /**
38  * Linux implementation of DosFileAttributeView for use on file systems such
39  * as ext3 that have extended attributes enabled and SAMBA configured to store
40  * DOS attributes.
41  */
42 
43 class LinuxDosFileAttributeView
44     extends UnixFileAttributeViews.Basic implements DosFileAttributeView
45 {
46     private static final Unsafe unsafe = Unsafe.getUnsafe();
47 
48     private static final String READONLY_NAME = "readonly";
49     private static final String ARCHIVE_NAME = "archive";
50     private static final String SYSTEM_NAME = "system";
51     private static final String HIDDEN_NAME = "hidden";
52 
53     private static final String DOS_XATTR_NAME = "user.DOSATTRIB";
54     private static final byte[] DOS_XATTR_NAME_AS_BYTES = Util.toBytes(DOS_XATTR_NAME);
55 
56     private static final int DOS_XATTR_READONLY = 0x01;
57     private static final int DOS_XATTR_HIDDEN   = 0x02;
58     private static final int DOS_XATTR_SYSTEM   = 0x04;
59     private static final int DOS_XATTR_ARCHIVE  = 0x20;
60 
61     // the names of the DOS attributes (includes basic)
62     private static final Set<String> dosAttributeNames =
63         Util.newSet(basicAttributeNames, READONLY_NAME, ARCHIVE_NAME, SYSTEM_NAME, HIDDEN_NAME);
64 
LinuxDosFileAttributeView(UnixPath file, boolean followLinks)65     LinuxDosFileAttributeView(UnixPath file, boolean followLinks) {
66         super(file, followLinks);
67     }
68 
69     @Override
name()70     public String name() {
71         return "dos";
72     }
73 
74     @Override
setAttribute(String attribute, Object value)75     public void setAttribute(String attribute, Object value)
76         throws IOException
77     {
78         if (attribute.equals(READONLY_NAME)) {
79             setReadOnly((Boolean)value);
80             return;
81         }
82         if (attribute.equals(ARCHIVE_NAME)) {
83             setArchive((Boolean)value);
84             return;
85         }
86         if (attribute.equals(SYSTEM_NAME)) {
87             setSystem((Boolean)value);
88             return;
89         }
90         if (attribute.equals(HIDDEN_NAME)) {
91             setHidden((Boolean)value);
92             return;
93         }
94         super.setAttribute(attribute, value);
95     }
96 
97     @Override
readAttributes(String[] attributes)98     public Map<String,Object> readAttributes(String[] attributes)
99         throws IOException
100     {
101         AttributesBuilder builder =
102             AttributesBuilder.create(dosAttributeNames, attributes);
103         DosFileAttributes attrs = readAttributes();
104         addRequestedBasicAttributes(attrs, builder);
105         if (builder.match(READONLY_NAME))
106             builder.add(READONLY_NAME, attrs.isReadOnly());
107         if (builder.match(ARCHIVE_NAME))
108             builder.add(ARCHIVE_NAME, attrs.isArchive());
109         if (builder.match(SYSTEM_NAME))
110             builder.add(SYSTEM_NAME, attrs.isSystem());
111         if (builder.match(HIDDEN_NAME))
112             builder.add(HIDDEN_NAME, attrs.isHidden());
113         return builder.unmodifiableMap();
114     }
115 
116     @Override
readAttributes()117     public DosFileAttributes readAttributes() throws IOException {
118         file.checkRead();
119 
120         int fd = -1;
121         try {
122              fd = file.openForAttributeAccess(followLinks);
123              final UnixFileAttributes attrs = UnixFileAttributes.get(fd);
124              final int dosAttribute = getDosAttribute(fd);
125 
126              return new DosFileAttributes() {
127                 @Override
128                 public FileTime lastModifiedTime() {
129                     return attrs.lastModifiedTime();
130                 }
131                 @Override
132                 public FileTime lastAccessTime() {
133                     return attrs.lastAccessTime();
134                 }
135                 @Override
136                 public FileTime creationTime() {
137                     return attrs.creationTime();
138                 }
139                 @Override
140                 public boolean isRegularFile() {
141                     return attrs.isRegularFile();
142                 }
143                 @Override
144                 public boolean isDirectory() {
145                     return attrs.isDirectory();
146                 }
147                 @Override
148                 public boolean isSymbolicLink() {
149                     return attrs.isSymbolicLink();
150                 }
151                 @Override
152                 public boolean isOther() {
153                     return attrs.isOther();
154                 }
155                 @Override
156                 public long size() {
157                     return attrs.size();
158                 }
159                 @Override
160                 public Object fileKey() {
161                     return attrs.fileKey();
162                 }
163                 @Override
164                 public boolean isReadOnly() {
165                     return (dosAttribute & DOS_XATTR_READONLY) != 0;
166                 }
167                 @Override
168                 public boolean isHidden() {
169                     return (dosAttribute & DOS_XATTR_HIDDEN) != 0;
170                 }
171                 @Override
172                 public boolean isArchive() {
173                     return (dosAttribute & DOS_XATTR_ARCHIVE) != 0;
174                 }
175                 @Override
176                 public boolean isSystem() {
177                     return (dosAttribute & DOS_XATTR_SYSTEM) != 0;
178                 }
179              };
180 
181         } catch (UnixException x) {
182             x.rethrowAsIOException(file);
183             return null;    // keep compiler happy
184         } finally {
185             close(fd);
186         }
187     }
188 
189     @Override
190     public void setReadOnly(boolean value) throws IOException {
191         updateDosAttribute(DOS_XATTR_READONLY, value);
192     }
193 
194     @Override
195     public void setHidden(boolean value) throws IOException {
196         updateDosAttribute(DOS_XATTR_HIDDEN, value);
197     }
198 
199     @Override
200     public void setArchive(boolean value) throws IOException {
201         updateDosAttribute(DOS_XATTR_ARCHIVE, value);
202     }
203 
204     @Override
205     public void setSystem(boolean value) throws IOException {
206         updateDosAttribute(DOS_XATTR_SYSTEM, value);
207     }
208 
209     /**
210      * Reads the value of the user.DOSATTRIB extended attribute
211      */
212     private int getDosAttribute(int fd) throws UnixException {
213         final int size = 24;
214 
215         NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
216         try {
217             int len = LinuxNativeDispatcher
218                 .fgetxattr(fd, DOS_XATTR_NAME_AS_BYTES, buffer.address(), size);
219 
220             if (len > 0) {
221                 // ignore null terminator
222                 if (unsafe.getByte(buffer.address()+len-1) == 0)
223                     len--;
224 
225                 // convert to String and parse
226                 byte[] buf = new byte[len];
227                 unsafe.copyMemory(null, buffer.address(), buf,
228                     Unsafe.ARRAY_BYTE_BASE_OFFSET, len);
229                 String value = Util.toString(buf);
230 
231                 // should be something like 0x20
232                 if (value.length() >= 3 && value.startsWith("0x")) {
233                     try {
234                         return Integer.parseInt(value.substring(2), 16);
235                     } catch (NumberFormatException x) {
236                         // ignore
237                     }
238                 }
239             }
240             throw new UnixException("Value of " + DOS_XATTR_NAME + " attribute is invalid");
241         } catch (UnixException x) {
242             // default value when attribute does not exist
243             if (x.errno() == ENODATA)
244                 return 0;
245             throw x;
246         } finally {
247             buffer.release();
248         }
249     }
250 
251     /**
252      * Updates the value of the user.DOSATTRIB extended attribute
253      */
254     private void updateDosAttribute(int flag, boolean enable) throws IOException {
255         file.checkWrite();
256 
257         int fd = -1;
258         try {
259             fd = file.openForAttributeAccess(followLinks);
260             int oldValue = getDosAttribute(fd);
261             int newValue = oldValue;
262             if (enable) {
263                 newValue |= flag;
264             } else {
265                 newValue &= ~flag;
266             }
267             if (newValue != oldValue) {
268                 byte[] value = Util.toBytes("0x" + Integer.toHexString(newValue));
269                 NativeBuffer buffer = NativeBuffers.asNativeBuffer(value);
270                 try {
271                     LinuxNativeDispatcher.fsetxattr(fd, DOS_XATTR_NAME_AS_BYTES,
272                         buffer.address(), value.length+1);
273                 } finally {
274                     buffer.release();
275                 }
276             }
277         } catch (UnixException x) {
278             x.rethrowAsIOException(file);
279         } finally {
280             close(fd);
281         }
282     }
283 }
284