1 /* 2 * Copyright (c) 2018 Václav Haisman, All Rights Reserved 3 * 4 * The contents of this file is dual-licensed under 2 5 * alternative Open Source/Free licenses: LGPL 2.1 or later and 6 * Apache License 2.0. 7 * 8 * You can freely decide which license you want to apply to 9 * the project. 10 * 11 * You may obtain a copy of the LGPL License at: 12 * 13 * http://www.gnu.org/licenses/licenses.html 14 * 15 * A copy is also included in the downloadable source code package 16 * containing JNA, in file "LGPL2.1". 17 * 18 * You may obtain a copy of the Apache License at: 19 * 20 * http://www.apache.org/licenses/ 21 * 22 * A copy is also included in the downloadable source code package 23 * containing JNA, in file "AL2.0". 24 */ 25 package com.sun.jna.platform.linux; 26 27 import com.sun.jna.Memory; 28 import com.sun.jna.Native; 29 import com.sun.jna.platform.linux.XAttr.size_t; 30 import com.sun.jna.platform.linux.XAttr.ssize_t; 31 32 import java.io.IOException; 33 import java.nio.ByteBuffer; 34 import java.nio.charset.Charset; 35 import java.util.Collection; 36 import java.util.LinkedHashSet; 37 import java.util.Set; 38 39 /** 40 * Utility functions class for handling file extended attributes on Linux. 41 */ 42 public abstract class XAttrUtil { 43 XAttrUtil()44 private XAttrUtil() { 45 } 46 47 /** 48 * Set or replace value of extended attribute. 49 * 50 * @param path file path 51 * @param name extended attribute name 52 * @param value value to set 53 * @throws IOException on any error 54 */ setXAttr(String path, String name, String value)55 public static void setXAttr(String path, String name, String value) throws IOException { 56 setXAttr(path, name, value, Native.getDefaultStringEncoding()); 57 } 58 59 /** 60 * Set or replace value of extended attribute. 61 * 62 * @param path file path 63 * @param name extended attribute name 64 * @param value value to set 65 * @param encoding character encoding to be used for stored value 66 * @throws IOException on any error 67 */ setXAttr(String path, String name, String value, String encoding)68 public static void setXAttr(String path, String name, String value, String encoding) 69 throws IOException { 70 setXAttr(path, name, value.getBytes(encoding)); 71 } 72 73 /** 74 * Set or replace value of extended attribute. 75 * 76 * @param path file path 77 * @param name extended attribute name 78 * @param value value to set 79 * @throws IOException on any error 80 */ setXAttr(String path, String name, byte[] value)81 public static void setXAttr(String path, String name, byte[] value) throws IOException { 82 int retval = XAttr.INSTANCE.setxattr(path, name, value, new size_t(value.length), 0); 83 if (retval != 0) { 84 final int eno = Native.getLastError(); 85 throw new IOException("errno: " + eno); 86 } 87 } 88 89 90 /** 91 * Set or replace value of extended attribute but in case of symbolic link set the extended 92 * attribute on the link itself instead linked file. 93 * 94 * @param path file path 95 * @param name extended attribute name 96 * @param value value to set 97 * @throws IOException on any error 98 */ lSetXAttr(String path, String name, String value)99 public static void lSetXAttr(String path, String name, String value) throws IOException { 100 lSetXAttr(path, name, value, Native.getDefaultStringEncoding()); 101 } 102 103 /** 104 * Set or replace value of extended attribute but in case of symbolic link set the extended 105 * attribute on the link itself instead linked file. 106 * 107 * @param path file path 108 * @param name extended attribute name 109 * @param value value to set 110 * @param encoding character encoding to be used for stored value 111 * @throws IOException on any error 112 */ lSetXAttr(String path, String name, String value, String encoding)113 public static void lSetXAttr(String path, String name, String value, String encoding) 114 throws IOException { 115 lSetXAttr(path, name, value.getBytes(encoding)); 116 } 117 118 /** 119 * Set or replace value of extended attribute but in case of symbolic link set the extended 120 * attribute on the link itself instead linked file. 121 * 122 * @param path file path 123 * @param name extended attribute name 124 * @param value value to set 125 * @throws IOException on any error 126 */ lSetXAttr(String path, String name, byte[] value)127 public static void lSetXAttr(String path, String name, byte[] value) throws IOException { 128 final int retval = XAttr.INSTANCE.lsetxattr(path, name, value, new size_t(value.length), 0); 129 if (retval != 0) { 130 final int eno = Native.getLastError(); 131 throw new IOException("errno: " + eno); 132 } 133 } 134 135 136 /** 137 * Set or replace value of extended attribute. 138 * 139 * @param fd file handle 140 * @param name extended attribute name 141 * @param value value to set 142 * @throws IOException on any error 143 */ fSetXAttr(int fd, String name, String value)144 public static void fSetXAttr(int fd, String name, String value) throws IOException { 145 fSetXAttr(fd, name, value, Native.getDefaultStringEncoding()); 146 } 147 148 /** 149 * Set or replace value of extended attribute. 150 * 151 * @param fd file handle 152 * @param name extended attribute name 153 * @param value value to set 154 * @param encoding character encoding to be used for stored value 155 * @throws IOException on any error 156 */ fSetXAttr(int fd, String name, String value, String encoding)157 public static void fSetXAttr(int fd, String name, String value, String encoding) 158 throws IOException { 159 fSetXAttr(fd, name, value.getBytes(encoding)); 160 } 161 162 /** 163 * Set or replace value of extended attribute. 164 * 165 * @param fd file handle 166 * @param name extended attribute name 167 * @param value value to set 168 * @throws IOException on any error 169 */ fSetXAttr(int fd, String name, byte[] value)170 public static void fSetXAttr(int fd, String name, byte[] value) throws IOException { 171 final int retval = XAttr.INSTANCE.fsetxattr(fd, name, value, new size_t(value.length), 0); 172 if (retval != 0) { 173 final int eno = Native.getLastError(); 174 throw new IOException("errno: " + eno); 175 } 176 } 177 178 179 /** 180 * Get extended attribute value. 181 * 182 * @param path file path 183 * @param name extended attribute name 184 * @return extended attribute value 185 * @throws IOException on any error except <code>ERANGE</code> which handled internally 186 */ getXAttr(String path, String name)187 public static String getXAttr(String path, String name) throws IOException { 188 return getXAttr(path, name, Native.getDefaultStringEncoding()); 189 } 190 191 /** 192 * Get extended attribute value. 193 * 194 * @param path file path 195 * @param name extended attribute name 196 * @param encoding character encoding to be used to decode stored extended attribute value 197 * @return extended attribute value 198 * @throws IOException on any error except <code>ERANGE</code> which handled internally 199 */ getXAttr(String path, String name, String encoding)200 public static String getXAttr(String path, String name, String encoding) throws IOException { 201 byte[] valueMem = getXAttrBytes(path, name); 202 return new String(valueMem, Charset.forName(encoding)); 203 } 204 205 /** 206 * Get extended attribute value. 207 * 208 * @param path file path 209 * @param name extended attribute name 210 * @return extended attribute value 211 * @throws IOException on any error except <code>ERANGE</code> which handled internally 212 */ getXAttrBytes(String path, String name)213 public static byte[] getXAttrBytes(String path, String name) throws IOException { 214 ssize_t retval; 215 byte[] valueMem; 216 int eno = 0; 217 218 do { 219 retval = XAttr.INSTANCE.getxattr(path, name, (byte[]) null, size_t.ZERO); 220 if (retval.longValue() < 0) { 221 eno = Native.getLastError(); 222 throw new IOException("errno: " + eno); 223 } 224 225 valueMem = new byte[retval.intValue()]; 226 retval = XAttr.INSTANCE.getxattr(path, name, valueMem, new size_t(valueMem.length)); 227 if (retval.longValue() < 0) { 228 eno = Native.getLastError(); 229 if (eno != XAttr.ERANGE) { 230 throw new IOException("errno: " + eno); 231 } 232 } 233 } while (retval.longValue() < 0 && eno == XAttr.ERANGE); 234 235 return valueMem; 236 } 237 238 /** 239 * Get extended attribute value. 240 * 241 * @param path file path 242 * @param name extended attribute name 243 * @return extended attribute value 244 * @throws IOException on any error except <code>ERANGE</code> which handled internally 245 */ getXAttrAsMemory(String path, String name)246 public static Memory getXAttrAsMemory(String path, String name) throws IOException { 247 ssize_t retval; 248 Memory valueMem; 249 int eno = 0; 250 251 do { 252 retval = XAttr.INSTANCE.getxattr(path, name, (Memory) null, size_t.ZERO); 253 if (retval.longValue() < 0) { 254 eno = Native.getLastError(); 255 throw new IOException("errno: " + eno); 256 } 257 258 if (retval.longValue() == 0) { 259 return null; 260 } 261 262 valueMem = new Memory(retval.longValue()); 263 retval = XAttr.INSTANCE.getxattr(path, name, valueMem, new size_t(valueMem.size())); 264 if (retval.longValue() < 0) { 265 eno = Native.getLastError(); 266 if (eno != XAttr.ERANGE) { 267 throw new IOException("errno: " + eno); 268 } 269 } 270 } while (retval.longValue() < 0 && eno == XAttr.ERANGE); 271 272 return valueMem; 273 } 274 275 276 /** 277 * Get extended attribute value but in case of symbolic link get the value from the link 278 * itself instead of linked file. 279 * 280 * @param path file path 281 * @param name extended attribute name 282 * @return extended attribute value 283 * @throws IOException on any error except <code>ERANGE</code> which handled internally 284 */ lGetXAttr(String path, String name)285 public static String lGetXAttr(String path, String name) throws IOException { 286 return lGetXAttr(path, name, Native.getDefaultStringEncoding()); 287 } 288 289 /** 290 * Get extended attribute value but in case of symbolic link get the value from the link 291 * itself instead of linked file. 292 * 293 * @param path file path 294 * @param name extended attribute name 295 * @param encoding character encoding to be used to decode stored extended attribute value 296 * @return extended attribute value 297 * @throws IOException on any error except <code>ERANGE</code> which handled internally 298 */ lGetXAttr(String path, String name, String encoding)299 public static String lGetXAttr(String path, String name, String encoding) throws IOException { 300 byte[] valueMem = lGetXAttrBytes(path, name); 301 return new String(valueMem, Charset.forName(encoding)); 302 } 303 304 /** 305 * Get extended attribute value but in case of symbolic link get the value from the link 306 * itself instead of linked file. 307 * 308 * @param path file path 309 * @param name extended attribute name 310 * @return extended attribute value 311 * @throws IOException on any error except <code>ERANGE</code> which handled internally 312 */ lGetXAttrBytes(String path, String name)313 public static byte[] lGetXAttrBytes(String path, String name) throws IOException { 314 ssize_t retval; 315 byte[] valueMem; 316 int eno = 0; 317 318 do { 319 retval = XAttr.INSTANCE.lgetxattr(path, name, (byte[]) null, size_t.ZERO); 320 if (retval.longValue() < 0) { 321 eno = Native.getLastError(); 322 throw new IOException("errno: " + eno); 323 } 324 325 valueMem = new byte[retval.intValue()]; 326 retval = XAttr.INSTANCE.lgetxattr(path, name, valueMem, new size_t(valueMem.length)); 327 if (retval.longValue() < 0) { 328 eno = Native.getLastError(); 329 if (eno != XAttr.ERANGE) { 330 throw new IOException("errno: " + eno); 331 } 332 } 333 } while (retval.longValue() < 0 && eno == XAttr.ERANGE); 334 335 return valueMem; 336 } 337 338 /** 339 * Get extended attribute value but in case of symbolic link get the value from the link 340 * itself instead of linked file. 341 * 342 * @param path file path 343 * @param name extended attribute name 344 * @return extended attribute value 345 * @throws IOException on any error except <code>ERANGE</code> which handled internally 346 */ lGetXAttrAsMemory(String path, String name)347 public static Memory lGetXAttrAsMemory(String path, String name) throws IOException { 348 ssize_t retval; 349 Memory valueMem; 350 int eno = 0; 351 352 do { 353 retval = XAttr.INSTANCE.lgetxattr(path, name, (Memory) null, size_t.ZERO); 354 if (retval.longValue() < 0) { 355 eno = Native.getLastError(); 356 throw new IOException("errno: " + eno); 357 } 358 359 if (retval.longValue() == 0) { 360 return null; 361 } 362 363 valueMem = new Memory(retval.longValue()); 364 retval = XAttr.INSTANCE.lgetxattr(path, name, valueMem, new size_t(valueMem.size())); 365 if (retval.longValue() < 0) { 366 eno = Native.getLastError(); 367 if (eno != XAttr.ERANGE) { 368 throw new IOException("errno: " + eno); 369 } 370 } 371 } while (retval.longValue() < 0 && eno == XAttr.ERANGE); 372 373 return valueMem; 374 } 375 376 377 /** 378 * Get extended attribute value. 379 * 380 * @param fd file handle 381 * @param name extended attribute name 382 * @return extended attribute value 383 * @throws IOException on any error except <code>ERANGE</code> which handled internally 384 */ fGetXAttr(int fd, String name)385 public static String fGetXAttr(int fd, String name) throws IOException { 386 return fGetXAttr(fd, name, Native.getDefaultStringEncoding()); 387 } 388 389 /** 390 * Get extended attribute value. 391 * 392 * @param fd file handle 393 * @param name extended attribute name 394 * @param encoding character encoding to be used to decode stored extended attribute value 395 * @return extended attribute value 396 * @throws IOException on any error except <code>ERANGE</code> which handled internally 397 */ fGetXAttr(int fd, String name, String encoding)398 public static String fGetXAttr(int fd, String name, String encoding) throws IOException { 399 byte[] valueMem = fGetXAttrBytes(fd, name); 400 return new String(valueMem, Charset.forName(encoding)); 401 } 402 403 /** 404 * Get extended attribute value. 405 * 406 * @param fd file handle 407 * @param name extended attribute name 408 * @return extended attribute value 409 * @throws IOException on any error except <code>ERANGE</code> which handled internally 410 */ fGetXAttrBytes(int fd, String name)411 public static byte[] fGetXAttrBytes(int fd, String name) throws IOException { 412 ssize_t retval; 413 byte[] valueMem; 414 int eno = 0; 415 416 do { 417 retval = XAttr.INSTANCE.fgetxattr(fd, name, (byte[]) null, size_t.ZERO); 418 if (retval.longValue() < 0) { 419 eno = Native.getLastError(); 420 throw new IOException("errno: " + eno); 421 } 422 423 valueMem = new byte[retval.intValue()]; 424 retval = XAttr.INSTANCE.fgetxattr(fd, name, valueMem, new size_t(valueMem.length)); 425 if (retval.longValue() < 0) { 426 eno = Native.getLastError(); 427 if (eno != XAttr.ERANGE) { 428 throw new IOException("errno: " + eno); 429 } 430 } 431 } while (retval.longValue() < 0 && eno == XAttr.ERANGE); 432 433 return valueMem; 434 } 435 436 /** 437 * Get extended attribute value. 438 * 439 * @param fd file handle 440 * @param name extended attribute name 441 * @return extended attribute value 442 * @throws IOException on any error except <code>ERANGE</code> which handled internally 443 */ fGetXAttrAsMemory(int fd, String name)444 public static Memory fGetXAttrAsMemory(int fd, String name) throws IOException { 445 ssize_t retval; 446 Memory valueMem; 447 int eno = 0; 448 449 do { 450 retval = XAttr.INSTANCE.fgetxattr(fd, name, (Memory) null, size_t.ZERO); 451 if (retval.longValue() < 0) { 452 eno = Native.getLastError(); 453 throw new IOException("errno: " + eno); 454 } 455 456 if (retval.longValue() == 0) { 457 return null; 458 } 459 460 valueMem = new Memory(retval.longValue()); 461 retval = XAttr.INSTANCE.fgetxattr(fd, name, valueMem, new size_t(valueMem.size())); 462 if (retval.longValue() < 0) { 463 eno = Native.getLastError(); 464 if (eno != XAttr.ERANGE) { 465 throw new IOException("errno: " + eno); 466 } 467 } 468 } while (retval.longValue() < 0 && eno == XAttr.ERANGE); 469 470 return valueMem; 471 } 472 473 474 /** 475 * List extended attributes on file. 476 * 477 * @param path file path 478 * @return collection of extended attributes' names 479 * @throws IOException on any error except <code>ERANGE</code> which handled internally 480 */ listXAttr(String path)481 public static Collection<String> listXAttr(String path) throws IOException { 482 return listXAttr(path, Native.getDefaultStringEncoding()); 483 } 484 485 /** 486 * List extended attributes on file. 487 * 488 * @param path file path 489 * @param encoding character encoding use to decode extended attributes' names 490 * @return collection of extended attributes' names 491 * @throws IOException on any error except <code>ERANGE</code> which handled internally 492 */ listXAttr(String path, String encoding)493 public static Collection<String> listXAttr(String path, String encoding) throws IOException { 494 ssize_t retval; 495 byte[] listMem; 496 int eno = 0; 497 498 do { 499 retval = XAttr.INSTANCE.listxattr(path, (byte[]) null, size_t.ZERO); 500 if (retval.longValue() < 0) { 501 eno = Native.getLastError(); 502 throw new IOException("errno: " + eno); 503 } 504 505 listMem = new byte[retval.intValue()]; 506 retval = XAttr.INSTANCE.listxattr(path, listMem, new size_t(listMem.length)); 507 if (retval.longValue() < 0) { 508 eno = Native.getLastError(); 509 if (eno != XAttr.ERANGE) { 510 throw new IOException("errno: " + eno); 511 } 512 } 513 } while (retval.longValue() < 0 && eno == XAttr.ERANGE); 514 515 return splitBufferToStrings(listMem, encoding); 516 } 517 518 519 /** 520 * List extended attributes on file but in case of symbolic link get extended attributes of 521 * the link itself instead of linked file. 522 * 523 * @param path file path 524 * @return collection of extended attributes' names 525 * @throws IOException on any error except <code>ERANGE</code> which handled internally 526 */ lListXAttr(String path)527 public static Collection<String> lListXAttr(String path) throws IOException { 528 return lListXAttr(path, Native.getDefaultStringEncoding()); 529 } 530 531 /** 532 * List extended attributes on file but in case of symbolic link get extended attributes of 533 * the link itself instead of linked file. 534 * 535 * @param path file path 536 * @param encoding character encoding use to decode extended attributes' names 537 * @return collection of extended attributes' names 538 * @throws IOException on any error except <code>ERANGE</code> which handled internally 539 */ lListXAttr(String path, String encoding)540 public static Collection<String> lListXAttr(String path, String encoding) throws IOException { 541 ssize_t retval; 542 byte[] listMem; 543 int eno = 0; 544 545 do { 546 retval = XAttr.INSTANCE.llistxattr(path, (byte[]) null, size_t.ZERO); 547 if (retval.longValue() < 0) { 548 eno = Native.getLastError(); 549 throw new IOException("errno: " + eno); 550 } 551 552 listMem = new byte[retval.intValue()]; 553 retval = XAttr.INSTANCE.llistxattr(path, listMem, new size_t(listMem.length)); 554 if (retval.longValue() < 0) { 555 eno = Native.getLastError(); 556 if (eno != XAttr.ERANGE) { 557 throw new IOException("errno: " + eno); 558 } 559 } 560 } while (retval.longValue() < 0 && eno == XAttr.ERANGE); 561 562 return splitBufferToStrings(listMem, encoding); 563 } 564 565 566 /** 567 * List extended attributes on file. 568 * 569 * @param fd file handle 570 * @return collection of extended attributes' names 571 * @throws IOException on any error except <code>ERANGE</code> which handled internally 572 */ fListXAttr(int fd)573 public static Collection<String> fListXAttr(int fd) throws IOException { 574 return fListXAttr(fd, Native.getDefaultStringEncoding()); 575 } 576 577 /** 578 * List extended attributes on file. 579 * 580 * @param fd file handle 581 * @param encoding character encoding use to decode extended attributes' names 582 * @return collection of extended attributes' names 583 * @throws IOException on any error except <code>ERANGE</code> which handled internally 584 */ fListXAttr(int fd, String encoding)585 public static Collection<String> fListXAttr(int fd, String encoding) throws IOException { 586 ssize_t retval; 587 byte[] listMem; 588 int eno = 0; 589 590 do { 591 retval = XAttr.INSTANCE.flistxattr(fd, (byte[]) null, size_t.ZERO); 592 if (retval.longValue() < 0) { 593 eno = Native.getLastError(); 594 throw new IOException("errno: " + eno); 595 } 596 597 listMem = new byte[retval.intValue()]; 598 retval = XAttr.INSTANCE.flistxattr(fd, listMem, new size_t(listMem.length)); 599 if (retval.longValue() < 0) { 600 eno = Native.getLastError(); 601 if (eno != XAttr.ERANGE) { 602 throw new IOException("errno: " + eno); 603 } 604 } 605 } while (retval.longValue() < 0 && eno == XAttr.ERANGE); 606 607 return splitBufferToStrings(listMem, encoding); 608 } 609 610 611 /** 612 * Remove extended attribute from file. 613 * 614 * @param path file path 615 * @param name extended attribute name 616 * @throws IOException on any error 617 */ removeXAttr(String path, String name)618 public static void removeXAttr(String path, String name) throws IOException { 619 final int retval = XAttr.INSTANCE.removexattr(path, name); 620 if (retval != 0) { 621 final int eno = Native.getLastError(); 622 throw new IOException("errno: " + eno); 623 } 624 } 625 626 /** 627 * Remove extended attribute from file but in case of symbolic link remove extended attribute 628 * from the link itself instead of linked file. 629 * 630 * @param path file path 631 * @param name extended attribute name 632 * @throws IOException on any error 633 */ lRemoveXAttr(String path, String name)634 public static void lRemoveXAttr(String path, String name) throws IOException { 635 final int retval = XAttr.INSTANCE.lremovexattr(path, name); 636 if (retval != 0) { 637 final int eno = Native.getLastError(); 638 throw new IOException("errno: " + eno); 639 } 640 } 641 642 /** 643 * Remove extended attribute from file. 644 * 645 * @param fd file handle 646 * @param name extended attribute name 647 * @throws IOException on any error 648 */ fRemoveXAttr(int fd, String name)649 public static void fRemoveXAttr(int fd, String name) throws IOException { 650 final int retval = XAttr.INSTANCE.fremovexattr(fd, name); 651 if (retval != 0) { 652 final int eno = Native.getLastError(); 653 throw new IOException("errno: " + eno); 654 } 655 } 656 splitBufferToStrings(byte[] valueMem, String encoding)657 private static Collection<String> splitBufferToStrings(byte[] valueMem, String encoding) 658 throws IOException { 659 final Charset charset = Charset.forName(encoding); 660 final Set<String> attributesList = new LinkedHashSet<String>(1); 661 int offset = 0; 662 for(int i = 0; i < valueMem.length; i++) { 663 // each entry is terminated by a single \0 byte 664 if(valueMem[i] == 0) { 665 // Convert bytes of the name to String. 666 final String name = new String(valueMem, offset, i - offset, charset); 667 attributesList.add(name); 668 offset = i + 1; 669 } 670 } 671 return attributesList; 672 } 673 } 674