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 
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 sun.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 = file.openForAttributeAccess(followLinks);
121         try {
122              final UnixFileAttributes attrs = UnixFileAttributes.get(fd);
123              final int dosAttribute = getDosAttribute(fd);
124 
125              return new DosFileAttributes() {
126                 @Override
127                 public FileTime lastModifiedTime() {
128                     return attrs.lastModifiedTime();
129                 }
130                 @Override
131                 public FileTime lastAccessTime() {
132                     return attrs.lastAccessTime();
133                 }
134                 @Override
135                 public FileTime creationTime() {
136                     return attrs.creationTime();
137                 }
138                 @Override
139                 public boolean isRegularFile() {
140                     return attrs.isRegularFile();
141                 }
142                 @Override
143                 public boolean isDirectory() {
144                     return attrs.isDirectory();
145                 }
146                 @Override
147                 public boolean isSymbolicLink() {
148                     return attrs.isSymbolicLink();
149                 }
150                 @Override
151                 public boolean isOther() {
152                     return attrs.isOther();
153                 }
154                 @Override
155                 public long size() {
156                     return attrs.size();
157                 }
158                 @Override
159                 public Object fileKey() {
160                     return attrs.fileKey();
161                 }
162                 @Override
163                 public boolean isReadOnly() {
164                     return (dosAttribute & DOS_XATTR_READONLY) != 0;
165                 }
166                 @Override
167                 public boolean isHidden() {
168                     return (dosAttribute & DOS_XATTR_HIDDEN) != 0;
169                 }
170                 @Override
171                 public boolean isArchive() {
172                     return (dosAttribute & DOS_XATTR_ARCHIVE) != 0;
173                 }
174                 @Override
175                 public boolean isSystem() {
176                     return (dosAttribute & DOS_XATTR_SYSTEM) != 0;
177                 }
178              };
179 
180         } catch (UnixException x) {
181             x.rethrowAsIOException(file);
182             return null;    // keep compiler happy
183         } finally {
184             close(fd);
185         }
186     }
187 
188     @Override
189     public void setReadOnly(boolean value) throws IOException {
190         updateDosAttribute(DOS_XATTR_READONLY, value);
191     }
192 
193     @Override
194     public void setHidden(boolean value) throws IOException {
195         updateDosAttribute(DOS_XATTR_HIDDEN, value);
196     }
197 
198     @Override
199     public void setArchive(boolean value) throws IOException {
200         updateDosAttribute(DOS_XATTR_ARCHIVE, value);
201     }
202 
203     @Override
204     public void setSystem(boolean value) throws IOException {
205         updateDosAttribute(DOS_XATTR_SYSTEM, value);
206     }
207 
208     /**
209      * Reads the value of the user.DOSATTRIB extended attribute
210      */
211     private int getDosAttribute(int fd) throws UnixException {
212         final int size = 24;
213 
214         NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
215         try {
216             int len = LinuxNativeDispatcher
217                 .fgetxattr(fd, DOS_XATTR_NAME_AS_BYTES, buffer.address(), size);
218 
219             if (len > 0) {
220                 // ignore null terminator
221                 if (unsafe.getByte(buffer.address()+len-1) == 0)
222                     len--;
223 
224                 // convert to String and parse
225                 byte[] buf = new byte[len];
226                 unsafe.copyMemory(null, buffer.address(), buf,
227                     Unsafe.ARRAY_BYTE_BASE_OFFSET, len);
228                 String value = Util.toString(buf);
229 
230                 // should be something like 0x20
231                 if (value.length() >= 3 && value.startsWith("0x")) {
232                     try {
233                         return Integer.parseInt(value.substring(2), 16);
234                     } catch (NumberFormatException x) {
235                         // ignore
236                     }
237                 }
238             }
239             throw new UnixException("Value of " + DOS_XATTR_NAME + " attribute is invalid");
240         } catch (UnixException x) {
241             // default value when attribute does not exist
242             if (x.errno() == ENODATA)
243                 return 0;
244             throw x;
245         } finally {
246             buffer.release();
247         }
248     }
249 
250     /**
251      * Updates the value of the user.DOSATTRIB extended attribute
252      */
253     private void updateDosAttribute(int flag, boolean enable) throws IOException {
254         file.checkWrite();
255 
256         int fd = file.openForAttributeAccess(followLinks);
257         try {
258             int oldValue = getDosAttribute(fd);
259             int newValue = oldValue;
260             if (enable) {
261                 newValue |= flag;
262             } else {
263                 newValue &= ~flag;
264             }
265             if (newValue != oldValue) {
266                 byte[] value = Util.toBytes("0x" + Integer.toHexString(newValue));
267                 NativeBuffer buffer = NativeBuffers.asNativeBuffer(value);
268                 try {
269                     LinuxNativeDispatcher.fsetxattr(fd, DOS_XATTR_NAME_AS_BYTES,
270                         buffer.address(), value.length+1);
271                 } finally {
272                     buffer.release();
273                 }
274             }
275         } catch (UnixException x) {
276             x.rethrowAsIOException(file);
277         } finally {
278             close(fd);
279         }
280     }
281 }
282