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 // Android-changed: We don't have Unsafe.copyMemory yet, so we use putByte. 227 // unsafe.copyMemory(null, buffer.address(), buf, 228 // Unsafe.ARRAY_BYTE_BASE_OFFSET, len); 229 for (int i = 0; i < len; i++) { 230 buf[i] = unsafe.getByte(buffer.address() + i); 231 } 232 String value = Util.toString(buf); 233 234 // should be something like 0x20 235 if (value.length() >= 3 && value.startsWith("0x")) { 236 try { 237 return Integer.parseInt(value.substring(2), 16); 238 } catch (NumberFormatException x) { 239 // ignore 240 } 241 } 242 } 243 throw new UnixException("Value of " + DOS_XATTR_NAME + " attribute is invalid"); 244 } catch (UnixException x) { 245 // default value when attribute does not exist 246 if (x.errno() == ENODATA) 247 return 0; 248 throw x; 249 } finally { 250 buffer.release(); 251 } 252 } 253 254 /** 255 * Updates the value of the user.DOSATTRIB extended attribute 256 */ 257 private void updateDosAttribute(int flag, boolean enable) throws IOException { 258 file.checkWrite(); 259 260 int fd = file.openForAttributeAccess(followLinks); 261 try { 262 int oldValue = getDosAttribute(fd); 263 int newValue = oldValue; 264 if (enable) { 265 newValue |= flag; 266 } else { 267 newValue &= ~flag; 268 } 269 if (newValue != oldValue) { 270 byte[] value = Util.toBytes("0x" + Integer.toHexString(newValue)); 271 NativeBuffer buffer = NativeBuffers.asNativeBuffer(value); 272 try { 273 LinuxNativeDispatcher.fsetxattr(fd, DOS_XATTR_NAME_AS_BYTES, 274 buffer.address(), value.length+1); 275 } finally { 276 buffer.release(); 277 } 278 } 279 } catch (UnixException x) { 280 x.rethrowAsIOException(file); 281 } finally { 282 close(fd); 283 } 284 } 285 } 286