/* * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.nio.fs; import java.nio.file.attribute.*; import java.util.Map; import java.util.Set; import java.io.IOException; import sun.misc.Unsafe; import static sun.nio.fs.UnixNativeDispatcher.*; import static sun.nio.fs.UnixConstants.*; /** * Linux implementation of DosFileAttributeView for use on file systems such * as ext3 that have extended attributes enabled and SAMBA configured to store * DOS attributes. */ class LinuxDosFileAttributeView extends UnixFileAttributeViews.Basic implements DosFileAttributeView { private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final String READONLY_NAME = "readonly"; private static final String ARCHIVE_NAME = "archive"; private static final String SYSTEM_NAME = "system"; private static final String HIDDEN_NAME = "hidden"; private static final String DOS_XATTR_NAME = "user.DOSATTRIB"; private static final byte[] DOS_XATTR_NAME_AS_BYTES = DOS_XATTR_NAME.getBytes(); private static final int DOS_XATTR_READONLY = 0x01; private static final int DOS_XATTR_HIDDEN = 0x02; private static final int DOS_XATTR_SYSTEM = 0x04; private static final int DOS_XATTR_ARCHIVE = 0x20; // the names of the DOS attributes (includes basic) private static final Set dosAttributeNames = Util.newSet(basicAttributeNames, READONLY_NAME, ARCHIVE_NAME, SYSTEM_NAME, HIDDEN_NAME); LinuxDosFileAttributeView(UnixPath file, boolean followLinks) { super(file, followLinks); } @Override public String name() { return "dos"; } @Override public void setAttribute(String attribute, Object value) throws IOException { if (attribute.equals(READONLY_NAME)) { setReadOnly((Boolean)value); return; } if (attribute.equals(ARCHIVE_NAME)) { setArchive((Boolean)value); return; } if (attribute.equals(SYSTEM_NAME)) { setSystem((Boolean)value); return; } if (attribute.equals(HIDDEN_NAME)) { setHidden((Boolean)value); return; } super.setAttribute(attribute, value); } @Override public Map readAttributes(String[] attributes) throws IOException { AttributesBuilder builder = AttributesBuilder.create(dosAttributeNames, attributes); DosFileAttributes attrs = readAttributes(); addRequestedBasicAttributes(attrs, builder); if (builder.match(READONLY_NAME)) builder.add(READONLY_NAME, attrs.isReadOnly()); if (builder.match(ARCHIVE_NAME)) builder.add(ARCHIVE_NAME, attrs.isArchive()); if (builder.match(SYSTEM_NAME)) builder.add(SYSTEM_NAME, attrs.isSystem()); if (builder.match(HIDDEN_NAME)) builder.add(HIDDEN_NAME, attrs.isHidden()); return builder.unmodifiableMap(); } @Override public DosFileAttributes readAttributes() throws IOException { file.checkRead(); int fd = file.openForAttributeAccess(followLinks); try { final UnixFileAttributes attrs = UnixFileAttributes.get(fd); final int dosAttribute = getDosAttribute(fd); return new DosFileAttributes() { @Override public FileTime lastModifiedTime() { return attrs.lastModifiedTime(); } @Override public FileTime lastAccessTime() { return attrs.lastAccessTime(); } @Override public FileTime creationTime() { return attrs.creationTime(); } @Override public boolean isRegularFile() { return attrs.isRegularFile(); } @Override public boolean isDirectory() { return attrs.isDirectory(); } @Override public boolean isSymbolicLink() { return attrs.isSymbolicLink(); } @Override public boolean isOther() { return attrs.isOther(); } @Override public long size() { return attrs.size(); } @Override public Object fileKey() { return attrs.fileKey(); } @Override public boolean isReadOnly() { return (dosAttribute & DOS_XATTR_READONLY) != 0; } @Override public boolean isHidden() { return (dosAttribute & DOS_XATTR_HIDDEN) != 0; } @Override public boolean isArchive() { return (dosAttribute & DOS_XATTR_ARCHIVE) != 0; } @Override public boolean isSystem() { return (dosAttribute & DOS_XATTR_SYSTEM) != 0; } }; } catch (UnixException x) { x.rethrowAsIOException(file); return null; // keep compiler happy } finally { close(fd); } } @Override public void setReadOnly(boolean value) throws IOException { updateDosAttribute(DOS_XATTR_READONLY, value); } @Override public void setHidden(boolean value) throws IOException { updateDosAttribute(DOS_XATTR_HIDDEN, value); } @Override public void setArchive(boolean value) throws IOException { updateDosAttribute(DOS_XATTR_ARCHIVE, value); } @Override public void setSystem(boolean value) throws IOException { updateDosAttribute(DOS_XATTR_SYSTEM, value); } /** * Reads the value of the user.DOSATTRIB extended attribute */ private int getDosAttribute(int fd) throws UnixException { final int size = 24; NativeBuffer buffer = NativeBuffers.getNativeBuffer(size); try { int len = LinuxNativeDispatcher .fgetxattr(fd, DOS_XATTR_NAME_AS_BYTES, buffer.address(), size); if (len > 0) { // ignore null terminator if (unsafe.getByte(buffer.address()+len-1) == 0) len--; // convert to String and parse byte[] buf = new byte[len]; unsafe.copyMemory(null, buffer.address(), buf, Unsafe.ARRAY_BYTE_BASE_OFFSET, len); String value = new String(buf); // platform encoding // should be something like 0x20 if (value.length() >= 3 && value.startsWith("0x")) { try { return Integer.parseInt(value.substring(2), 16); } catch (NumberFormatException x) { // ignore } } } throw new UnixException("Value of " + DOS_XATTR_NAME + " attribute is invalid"); } catch (UnixException x) { // default value when attribute does not exist if (x.errno() == ENODATA) return 0; throw x; } finally { buffer.release(); } } /** * Updates the value of the user.DOSATTRIB extended attribute */ private void updateDosAttribute(int flag, boolean enable) throws IOException { file.checkWrite(); int fd = file.openForAttributeAccess(followLinks); try { int oldValue = getDosAttribute(fd); int newValue = oldValue; if (enable) { newValue |= flag; } else { newValue &= ~flag; } if (newValue != oldValue) { byte[] value = ("0x" + Integer.toHexString(newValue)).getBytes(); NativeBuffer buffer = NativeBuffers.asNativeBuffer(value); try { LinuxNativeDispatcher.fsetxattr(fd, DOS_XATTR_NAME_AS_BYTES, buffer.address(), value.length+1); } finally { buffer.release(); } } } catch (UnixException x) { x.rethrowAsIOException(file); } finally { close(fd); } } }