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