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