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 
  65     LinuxDosFileAttributeView(UnixPath file, boolean followLinks) {
  66         super(file, followLinks);
  67     }
  68 
  69     @Override
  70     public String name() {
  71         return "dos";
  72     }
  73 
  74     @Override
  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
  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
 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 }