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.*;
29 import java.nio.ByteBuffer;
30 import java.io.IOException;
31 import java.util.*;
32 import sun.misc.Unsafe;
33 
34 import static sun.nio.fs.UnixConstants.*;
35 import static sun.nio.fs.LinuxNativeDispatcher.*;
36 
37 /**
38  * Linux implementation of UserDefinedFileAttributeView using extended attributes.
39  */
40 
41 class LinuxUserDefinedFileAttributeView
42     extends AbstractUserDefinedFileAttributeView
43 {
44     private static final Unsafe unsafe = Unsafe.getUnsafe();
45 
46     // namespace for extended user attributes
47     private static final String USER_NAMESPACE = "user.";
48 
49     // maximum bytes in extended attribute name (includes namespace)
50     private static final int XATTR_NAME_MAX = 255;
51 
nameAsBytes(UnixPath file, String name)52     private byte[] nameAsBytes(UnixPath file, String name) throws IOException {
53         if (name == null)
54             throw new NullPointerException("'name' is null");
55         name = USER_NAMESPACE + name;
56         byte[] bytes = Util.toBytes(name);
57         if (bytes.length > XATTR_NAME_MAX) {
58             throw new FileSystemException(file.getPathForExceptionMessage(),
59                 null, "'" + name + "' is too big");
60         }
61         return bytes;
62     }
63 
64     // Parses buffer as array of NULL-terminated C strings.
asList(long address, int size)65     private List<String> asList(long address, int size) {
66         List<String> list = new ArrayList<>();
67         int start = 0;
68         int pos = 0;
69         while (pos < size) {
70             if (unsafe.getByte(address + pos) == 0) {
71                 int len = pos - start;
72                 byte[] value = new byte[len];
73                 // unsafe.copyMemory(null, address+start, value,
74                 //     Unsafe.ARRAY_BYTE_BASE_OFFSET, len);
75 
76                 // Android-changed: We don't have Unsafe.copyMemory yet, so we use getByte.
77                 for (int i = 0; i < len; i++) {
78                     value[i] = unsafe.getByte(address + start + i);
79                 }
80                 String s = Util.toString(value);
81                 if (s.startsWith(USER_NAMESPACE)) {
82                     s = s.substring(USER_NAMESPACE.length());
83                     list.add(s);
84                 }
85                 start = pos + 1;
86             }
87             pos++;
88         }
89         return list;
90     }
91 
92     private final UnixPath file;
93     private final boolean followLinks;
94 
LinuxUserDefinedFileAttributeView(UnixPath file, boolean followLinks)95     LinuxUserDefinedFileAttributeView(UnixPath file, boolean followLinks) {
96         this.file = file;
97         this.followLinks = followLinks;
98     }
99 
100     @Override
list()101     public List<String> list() throws IOException  {
102         if (System.getSecurityManager() != null)
103             checkAccess(file.getPathForPermissionCheck(), true, false);
104 
105         int fd = file.openForAttributeAccess(followLinks);
106         NativeBuffer buffer = null;
107         try {
108             int size = 1024;
109             buffer = NativeBuffers.getNativeBuffer(size);
110             for (;;) {
111                 try {
112                     int n = flistxattr(fd, buffer.address(), size);
113                     List<String> list = asList(buffer.address(), n);
114                     return Collections.unmodifiableList(list);
115                 } catch (UnixException x) {
116                     // allocate larger buffer if required
117                     if (x.errno() == ERANGE && size < 32*1024) {
118                         buffer.release();
119                         size *= 2;
120                         buffer = null;
121                         buffer = NativeBuffers.getNativeBuffer(size);
122                         continue;
123                     }
124                     throw new FileSystemException(file.getPathForExceptionMessage(),
125                         null, "Unable to get list of extended attributes: " +
126                         x.getMessage());
127                 }
128             }
129         } finally {
130             if (buffer != null)
131                 buffer.release();
132             close(fd);
133         }
134     }
135 
136     @Override
size(String name)137     public int size(String name) throws IOException  {
138         if (System.getSecurityManager() != null)
139             checkAccess(file.getPathForPermissionCheck(), true, false);
140 
141         int fd = file.openForAttributeAccess(followLinks);
142         try {
143             // fgetxattr returns size if called with size==0
144             return fgetxattr(fd, nameAsBytes(file,name), 0L, 0);
145         } catch (UnixException x) {
146             throw new FileSystemException(file.getPathForExceptionMessage(),
147                 null, "Unable to get size of extended attribute '" + name +
148                 "': " + x.getMessage());
149         } finally {
150             close(fd);
151         }
152     }
153 
154     @Override
read(String name, ByteBuffer dst)155     public int read(String name, ByteBuffer dst) throws IOException {
156         if (System.getSecurityManager() != null)
157             checkAccess(file.getPathForPermissionCheck(), true, false);
158 
159         if (dst.isReadOnly())
160             throw new IllegalArgumentException("Read-only buffer");
161         int pos = dst.position();
162         int lim = dst.limit();
163         assert (pos <= lim);
164         int rem = (pos <= lim ? lim - pos : 0);
165 
166         NativeBuffer nb;
167         long address;
168         if (dst instanceof sun.nio.ch.DirectBuffer) {
169             nb = null;
170             address = ((sun.nio.ch.DirectBuffer)dst).address() + pos;
171         } else {
172             // substitute with native buffer
173             nb = NativeBuffers.getNativeBuffer(rem);
174             address = nb.address();
175         }
176 
177         int fd = file.openForAttributeAccess(followLinks);
178         try {
179             try {
180                 int n = fgetxattr(fd, nameAsBytes(file,name), address, rem);
181 
182                 // if remaining is zero then fgetxattr returns the size
183                 if (rem == 0) {
184                     if (n > 0)
185                         throw new UnixException(ERANGE);
186                     return 0;
187                 }
188 
189                 // copy from buffer into backing array if necessary
190                 if (nb != null) {
191                     // Android-changed: We don't have Unsafe.copyMemory yet, so we use getByte.
192                     // int off = dst.arrayOffset() + pos + Unsafe.ARRAY_BYTE_BASE_OFFSET;
193                     // unsafe.copyMemory(null, address, dst.array(), off, n);
194                     for (int i = 0; i < n; i++) {
195                         dst.put(unsafe.getByte(address + i));
196                     }
197                 }
198                 dst.position(pos + n);
199                 return n;
200             } catch (UnixException x) {
201                 String msg = (x.errno() == ERANGE) ?
202                     "Insufficient space in buffer" : x.getMessage();
203                 throw new FileSystemException(file.getPathForExceptionMessage(),
204                     null, "Error reading extended attribute '" + name + "': " + msg);
205             } finally {
206                 close(fd);
207             }
208         } finally {
209             if (nb != null)
210                 nb.release();
211         }
212     }
213 
214     @Override
write(String name, ByteBuffer src)215     public int write(String name, ByteBuffer src) throws IOException {
216         if (System.getSecurityManager() != null)
217             checkAccess(file.getPathForPermissionCheck(), false, true);
218 
219         int pos = src.position();
220         int lim = src.limit();
221         assert (pos <= lim);
222         int rem = (pos <= lim ? lim - pos : 0);
223 
224         NativeBuffer nb;
225         long address;
226         if (src instanceof sun.nio.ch.DirectBuffer) {
227             nb = null;
228             address = ((sun.nio.ch.DirectBuffer)src).address() + pos;
229         } else {
230             // substitute with native buffer
231             nb = NativeBuffers.getNativeBuffer(rem);
232             address = nb.address();
233 
234             if (src.hasArray()) {
235                 // copy from backing array into buffer
236                 // Android-changed: We don't have Unsafe.copyMemory yet, so we use putByte.
237                 // int off = src.arrayOffset() + pos + Unsafe.ARRAY_BYTE_BASE_OFFSET;
238                 // unsafe.copyMemory(src.array(), off, null, address, rem);
239                 for (int i = 0; i < rem; i++) {
240                     unsafe.putByte(address + i, src.get());
241                 }
242             } else {
243                 // backing array not accessible so transfer via temporary array
244                 byte[] tmp = new byte[rem];
245                 src.get(tmp);
246                 src.position(pos);  // reset position as write may fail
247                 // Android-changed: We don't have Unsafe.copyMemory yet, so we use putByte.
248                 // unsafe.copyMemory(tmp, Unsafe.ARRAY_BYTE_BASE_OFFSET, null,
249                 //       address, rem);
250                 for (int i = 0; i < rem; i++) {
251                     unsafe.putByte(address + i, tmp[i]);
252                 }
253             }
254         }
255 
256         int fd = file.openForAttributeAccess(followLinks);
257         try {
258             try {
259                 fsetxattr(fd, nameAsBytes(file,name), address, rem);
260                 src.position(pos + rem);
261                 return rem;
262             } catch (UnixException x) {
263                 throw new FileSystemException(file.getPathForExceptionMessage(),
264                     null, "Error writing extended attribute '" + name + "': " +
265                     x.getMessage());
266             } finally {
267                 close(fd);
268             }
269         } finally {
270             if (nb != null)
271                 nb.release();
272         }
273     }
274 
275     @Override
delete(String name)276     public void delete(String name) throws IOException {
277         if (System.getSecurityManager() != null)
278             checkAccess(file.getPathForPermissionCheck(), false, true);
279 
280         int fd = file.openForAttributeAccess(followLinks);
281         try {
282             fremovexattr(fd, nameAsBytes(file,name));
283         } catch (UnixException x) {
284             throw new FileSystemException(file.getPathForExceptionMessage(),
285                 null, "Unable to delete extended attribute '" + name + "': " + x.getMessage());
286         } finally {
287             close(fd);
288         }
289     }
290 
291     /**
292      * Used by copyTo/moveTo to copy extended attributes from source to target.
293      *
294      * @param   ofd
295      *          file descriptor for source file
296      * @param   nfd
297      *          file descriptor for target file
298      */
copyExtendedAttributes(int ofd, int nfd)299     static void copyExtendedAttributes(int ofd, int nfd) {
300         NativeBuffer buffer = null;
301         try {
302 
303             // call flistxattr to get list of extended attributes.
304             int size = 1024;
305             buffer = NativeBuffers.getNativeBuffer(size);
306             for (;;) {
307                 try {
308                     size = flistxattr(ofd, buffer.address(), size);
309                     break;
310                 } catch (UnixException x) {
311                     // allocate larger buffer if required
312                     if (x.errno() == ERANGE && size < 32*1024) {
313                         buffer.release();
314                         size *= 2;
315                         buffer = null;
316                         buffer = NativeBuffers.getNativeBuffer(size);
317                         continue;
318                     }
319 
320                     // unable to get list of attributes
321                     return;
322                 }
323             }
324 
325             // parse buffer as array of NULL-terminated C strings.
326             long address = buffer.address();
327             int start = 0;
328             int pos = 0;
329             while (pos < size) {
330                 if (unsafe.getByte(address + pos) == 0) {
331                     // extract attribute name and copy attribute to target.
332                     // FIXME: We can avoid needless copying by using address+pos
333                     // as the address of the name.
334                     int len = pos - start;
335                     byte[] name = new byte[len];
336                     // Android-changed: We don't have Unsafe.copyMemory yet, so we use getByte.
337                     // unsafe.copyMemory(null, address+start, name,
338                     //    Unsafe.ARRAY_BYTE_BASE_OFFSET, len);
339                     for (int i = 0; i < len; i++) {
340                         name[i] = unsafe.getByte(address + start + i);
341                     }
342                     try {
343                         copyExtendedAttribute(ofd, name, nfd);
344                     } catch (UnixException ignore) {
345                         // ignore
346                     }
347                     start = pos + 1;
348                 }
349                 pos++;
350             }
351 
352         } finally {
353             if (buffer != null)
354                 buffer.release();
355         }
356     }
357 
copyExtendedAttribute(int ofd, byte[] name, int nfd)358     private static void copyExtendedAttribute(int ofd, byte[] name, int nfd)
359         throws UnixException
360     {
361         int size = fgetxattr(ofd, name, 0L, 0);
362         NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
363         try {
364             long address = buffer.address();
365             size = fgetxattr(ofd, name, address, size);
366             fsetxattr(nfd, name, address, size);
367         } finally {
368             buffer.release();
369         }
370     }
371 }
372