001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2008 Sun Microsystems, Inc. 025 * Portions Copyright 2013-2015 ForgeRock AS. 026 */ 027package org.opends.server.types; 028 029import java.io.File; 030import java.io.FileNotFoundException; 031import java.io.IOException; 032import java.nio.file.Files; 033import java.nio.file.Path; 034import java.nio.file.attribute.AclFileAttributeView; 035import java.nio.file.attribute.PosixFileAttributeView; 036import java.nio.file.attribute.PosixFilePermission; 037import java.nio.file.attribute.PosixFilePermissions; 038import java.util.Set; 039 040import org.forgerock.i18n.LocalizableMessage; 041import org.forgerock.opendj.ldap.ResultCode; 042 043import static org.opends.messages.UtilityMessages.*; 044 045/** 046 * This class provides a mechanism for setting file permissions in a 047 * more abstract manner than is provided by the underlying operating 048 * system and/or filesystem. It uses a traditional UNIX-style rwx/ugo 049 * representation for the permissions and converts them as necessary 050 * to the scheme used by the underlying platform. It does not provide 051 * any mechanism for getting file permissions, nor does it provide any 052 * way of dealing with file ownership or ACLs. 053 */ 054@org.opends.server.types.PublicAPI( 055 stability=org.opends.server.types.StabilityLevel.VOLATILE, 056 mayInstantiate=true, 057 mayExtend=false, 058 mayInvoke=true) 059public class FilePermission 060{ 061 /** 062 * The bitmask that should be used for indicating whether a file is 063 * readable by its owner. 064 */ 065 public static final int OWNER_READABLE = 0x0100; 066 067 068 069 /** 070 * The bitmask that should be used for indicating whether a file is 071 * writable by its owner. 072 */ 073 public static final int OWNER_WRITABLE = 0x0080; 074 075 076 077 /** 078 * The bitmask that should be used for indicating whether a file is 079 * executable by its owner. 080 */ 081 public static final int OWNER_EXECUTABLE = 0x0040; 082 083 084 085 /** 086 * The bitmask that should be used for indicating whether a file is 087 * readable by members of its group. 088 */ 089 public static final int GROUP_READABLE = 0x0020; 090 091 092 093 /** 094 * The bitmask that should be used for indicating whether a file is 095 * writable by members of its group. 096 */ 097 public static final int GROUP_WRITABLE = 0x0010; 098 099 100 101 /** 102 * The bitmask that should be used for indicating whether a file is 103 * executable by members of its group. 104 */ 105 public static final int GROUP_EXECUTABLE = 0x0008; 106 107 108 109 /** 110 * The bitmask that should be used for indicating whether a file is 111 * readable by users other than the owner or group members. 112 */ 113 public static final int OTHER_READABLE = 0x0004; 114 115 116 117 /** 118 * The bitmask that should be used for indicating whether a file is 119 * writable by users other than the owner or group members. 120 */ 121 public static final int OTHER_WRITABLE = 0x0002; 122 123 124 125 /** 126 * The bitmask that should be used for indicating whether a file is 127 * executable by users other than the owner or group members. 128 */ 129 public static final int OTHER_EXECUTABLE = 0x0001; 130 131 132 133 /** The encoded representation for this file permission. */ 134 private int encodedPermission; 135 136 137 138 /** 139 * Creates a new file permission object with the provided encoded 140 * representation. 141 * 142 * @param encodedPermission The encoded representation for this 143 * file permission. 144 */ 145 public FilePermission(int encodedPermission) 146 { 147 this.encodedPermission = encodedPermission; 148 } 149 150 151 152 /** 153 * Creates a new file permission with the specified rights for the 154 * file owner. Users other than the owner will not have any rights. 155 * 156 * @param ownerReadable Indicates whether the owner should have 157 * the read permission. 158 * @param ownerWritable Indicates whether the owner should have 159 * the write permission. 160 * @param ownerExecutable Indicates whether the owner should have 161 * the execute permission. 162 */ 163 public FilePermission(boolean ownerReadable, boolean ownerWritable, 164 boolean ownerExecutable) 165 { 166 encodedPermission = 0x0000; 167 168 if (ownerReadable) 169 { 170 encodedPermission |= OWNER_READABLE; 171 } 172 173 if (ownerWritable) 174 { 175 encodedPermission |= OWNER_WRITABLE; 176 } 177 178 if (ownerExecutable) 179 { 180 encodedPermission |= OWNER_EXECUTABLE; 181 } 182 } 183 184 185 186 /** 187 * Creates a new file permission with the specified rights for the 188 * file owner, group members, and other users. 189 * 190 * @param ownerReadable Indicates whether the owner should have 191 * the read permission. 192 * @param ownerWritable Indicates whether the owner should have 193 * the write permission. 194 * @param ownerExecutable Indicates whether the owner should have 195 * the execute permission. 196 * @param groupReadable Indicates whether members of the file's 197 * group should have the read permission. 198 * @param groupWritable Indicates whether members of the file's 199 * group should have the write permission. 200 * @param groupExecutable Indicates whether members of the file's 201 * group should have the execute 202 * permission. 203 * @param otherReadable Indicates whether other users should 204 * have the read permission. 205 * @param otherWritable Indicates whether other users should 206 * have the write permission. 207 * @param otherExecutable Indicates whether other users should 208 * have the execute permission. 209 */ 210 public FilePermission(boolean ownerReadable, boolean ownerWritable, 211 boolean ownerExecutable, 212 boolean groupReadable, boolean groupWritable, 213 boolean groupExecutable, 214 boolean otherReadable, boolean otherWritable, 215 boolean otherExecutable) 216 { 217 encodedPermission = 0x0000; 218 219 if (ownerReadable) 220 { 221 encodedPermission |= OWNER_READABLE; 222 } 223 224 if (ownerWritable) 225 { 226 encodedPermission |= OWNER_WRITABLE; 227 } 228 229 if (ownerExecutable) 230 { 231 encodedPermission |= OWNER_EXECUTABLE; 232 } 233 234 if (groupReadable) 235 { 236 encodedPermission |= GROUP_READABLE; 237 } 238 239 if (groupWritable) 240 { 241 encodedPermission |= GROUP_WRITABLE; 242 } 243 244 if (groupExecutable) 245 { 246 encodedPermission |= GROUP_EXECUTABLE; 247 } 248 249 if (otherReadable) 250 { 251 encodedPermission |= OTHER_READABLE; 252 } 253 254 if (otherWritable) 255 { 256 encodedPermission |= OTHER_WRITABLE; 257 } 258 259 if (otherExecutable) 260 { 261 encodedPermission |= OTHER_EXECUTABLE; 262 } 263 } 264 265 266 267 /** 268 * Indicates whether this file permission includes the owner read 269 * permission. 270 * 271 * @return <CODE>true</CODE> if this file permission includes the 272 * owner read permission, or <CODE>false</CODE> if not. 273 */ 274 public boolean isOwnerReadable() 275 { 276 return is(encodedPermission, OWNER_READABLE); 277 } 278 279 280 281 /** 282 * Indicates whether this file permission includes the owner write 283 * permission. 284 * 285 * @return <CODE>true</CODE> if this file permission includes the 286 * owner write permission, or <CODE>false</CODE> if not. 287 */ 288 public boolean isOwnerWritable() 289 { 290 return is(encodedPermission, OWNER_WRITABLE); 291 } 292 293 294 295 /** 296 * Indicates whether this file permission includes the owner execute 297 * permission. 298 * 299 * @return <CODE>true</CODE> if this file permission includes the 300 * owner execute permission, or <CODE>false</CODE> if not. 301 */ 302 public boolean isOwnerExecutable() 303 { 304 return is(encodedPermission, OWNER_EXECUTABLE); 305 } 306 307 308 309 /** 310 * Indicates whether this file permission includes the group read 311 * permission. 312 * 313 * @return <CODE>true</CODE> if this file permission includes the 314 * group read permission, or <CODE>false</CODE> if not. 315 */ 316 public boolean isGroupReadable() 317 { 318 return is(encodedPermission, GROUP_READABLE); 319 } 320 321 322 323 /** 324 * Indicates whether this file permission includes the group write 325 * permission. 326 * 327 * @return <CODE>true</CODE> if this file permission includes the 328 * group write permission, or <CODE>false</CODE> if not. 329 */ 330 public boolean isGroupWritable() 331 { 332 return is(encodedPermission, GROUP_WRITABLE); 333 } 334 335 336 337 /** 338 * Indicates whether this file permission includes the group execute 339 * permission. 340 * 341 * @return <CODE>true</CODE> if this file permission includes the 342 * group execute permission, or <CODE>false</CODE> if not. 343 */ 344 public boolean isGroupExecutable() 345 { 346 return is(encodedPermission, GROUP_EXECUTABLE); 347 } 348 349 350 351 /** 352 * Indicates whether this file permission includes the other read 353 * permission. 354 * 355 * @return <CODE>true</CODE> if this file permission includes the 356 * other read permission, or <CODE>false</CODE> if not. 357 */ 358 public boolean isOtherReadable() 359 { 360 return is(encodedPermission, OTHER_READABLE); 361 } 362 363 364 365 /** 366 * Indicates whether this file permission includes the other write 367 * permission. 368 * 369 * @return <CODE>true</CODE> if this file permission includes the 370 * other write permission, or <CODE>false</CODE> if not. 371 */ 372 public boolean isOtherWritable() 373 { 374 return is(encodedPermission, OTHER_WRITABLE); 375 } 376 377 378 379 /** 380 * Indicates whether this file permission includes the other execute 381 * permission. 382 * 383 * @return <CODE>true</CODE> if this file permission includes the 384 * other execute permission, or <CODE>false</CODE> if not. 385 */ 386 public boolean isOtherExecutable() 387 { 388 return is(encodedPermission, OTHER_EXECUTABLE); 389 } 390 391 private boolean is(int encodedPermissions, int permission) 392 { 393 return (encodedPermissions & permission) == permission; 394 } 395 396 /** 397 * Attempts to set the given permissions on the specified file. If 398 * the underlying platform does not allow the full level of 399 * granularity specified in the permissions, then an attempt will be 400 * made to set them as closely as possible to the provided 401 * permissions, erring on the side of security. 402 * 403 * @param f The file to which the permissions should be applied. 404 * @param p The permissions to apply to the file. 405 * 406 * @return <CODE>true</CODE> if the permissions (or the nearest 407 * equivalent) were successfully applied to the specified 408 * file, or <CODE>false</CODE> if was not possible to set 409 * the permissions on the current platform. 410 * 411 * @throws FileNotFoundException If the specified file does not 412 * exist. 413 * 414 * @throws DirectoryException If a problem occurs while trying to 415 * set the file permissions. 416 */ 417 public static boolean setPermissions(File f, FilePermission p) 418 throws FileNotFoundException, DirectoryException 419 { 420 if (!f.exists()) 421 { 422 throw new FileNotFoundException(ERR_FILEPERM_SET_NO_SUCH_FILE.get(f.getAbsolutePath()).toString()); 423 } 424 Path filePath = f.toPath(); 425 PosixFileAttributeView posix = Files.getFileAttributeView(filePath, PosixFileAttributeView.class); 426 if (posix != null) 427 { 428 StringBuilder posixMode = new StringBuilder(); 429 toPOSIXString(p, posixMode, "", "", ""); 430 Set<PosixFilePermission> perms = PosixFilePermissions.fromString(posixMode.toString()); 431 try 432 { 433 Files.setPosixFilePermissions(filePath, perms); 434 } 435 catch (UnsupportedOperationException | ClassCastException | IOException | SecurityException ex) 436 { 437 throw new DirectoryException(ResultCode.OTHER, ERR_FILEPERM_SET_JAVA_EXCEPTION.get(f.getAbsolutePath()), ex); 438 } 439 return true; 440 } 441 return Files.getFileAttributeView(filePath, AclFileAttributeView.class) != null; 442 } 443 444 445 446 /** 447 * Retrieves a three-character string that is the UNIX mode for the 448 * provided file permission. Each character of the string will be a 449 * numeric digit from zero through seven. 450 * 451 * @param p The permission to retrieve as a UNIX mode string. 452 * 453 * @return The UNIX mode string for the provided permission. 454 */ 455 public static String toUNIXMode(FilePermission p) 456 { 457 StringBuilder buffer = new StringBuilder(3); 458 toUNIXMode(buffer, p); 459 return buffer.toString(); 460 } 461 462 463 464 /** 465 * Appends a three-character string that is the UNIX mode for the 466 * provided file permission to the given buffer. Each character of 467 * the string will be a numeric digit from zero through seven. 468 * 469 * @param buffer The buffer to which the mode string should be 470 * appended. 471 * @param p The permission to retrieve as a UNIX mode string. 472 */ 473 public static void toUNIXMode(StringBuilder buffer, 474 FilePermission p) 475 { 476 byte modeByte = 0x00; 477 if (p.isOwnerReadable()) 478 { 479 modeByte |= 0x04; 480 } 481 if (p.isOwnerWritable()) 482 { 483 modeByte |= 0x02; 484 } 485 if (p.isOwnerExecutable()) 486 { 487 modeByte |= 0x01; 488 } 489 buffer.append(modeByte); 490 491 modeByte = 0x00; 492 if (p.isGroupReadable()) 493 { 494 modeByte |= 0x04; 495 } 496 if (p.isGroupWritable()) 497 { 498 modeByte |= 0x02; 499 } 500 if (p.isGroupExecutable()) 501 { 502 modeByte |= 0x01; 503 } 504 buffer.append(modeByte); 505 506 modeByte = 0x00; 507 if (p.isOtherReadable()) 508 { 509 modeByte |= 0x04; 510 } 511 if (p.isOtherWritable()) 512 { 513 modeByte |= 0x02; 514 } 515 if (p.isOtherExecutable()) 516 { 517 modeByte |= 0x01; 518 } 519 buffer.append(modeByte); 520 } 521 522 523 524 /** 525 * Decodes the provided string as a UNIX mode and retrieves the 526 * corresponding file permission. The mode string must contain 527 * three digits between zero and seven. 528 * 529 * @param modeString The string representation of the UNIX mode to 530 * decode. 531 * 532 * @return The file permission that is equivalent to the given UNIX 533 * mode. 534 * 535 * @throws DirectoryException If the provided string is not a 536 * valid three-digit UNIX mode. 537 */ 538 public static FilePermission decodeUNIXMode(String modeString) 539 throws DirectoryException 540 { 541 if (modeString == null || modeString.length() != 3) 542 { 543 LocalizableMessage message = ERR_FILEPERM_INVALID_UNIX_MODE_STRING.get(modeString); 544 throw new DirectoryException(ResultCode.OTHER, message); 545 } 546 547 int encodedPermission = 0x0000; 548 switch (modeString.charAt(0)) 549 { 550 case '0': 551 break; 552 case '1': 553 encodedPermission |= OWNER_EXECUTABLE; 554 break; 555 case '2': 556 encodedPermission |= OWNER_WRITABLE; 557 break; 558 case '3': 559 encodedPermission |= OWNER_WRITABLE | OWNER_EXECUTABLE; 560 break; 561 case '4': 562 encodedPermission |= OWNER_READABLE; 563 break; 564 case '5': 565 encodedPermission |= OWNER_READABLE | OWNER_EXECUTABLE; 566 break; 567 case '6': 568 encodedPermission |= OWNER_READABLE | OWNER_WRITABLE; 569 break; 570 case '7': 571 encodedPermission |= OWNER_READABLE | OWNER_WRITABLE | 572 OWNER_EXECUTABLE; 573 break; 574 default: 575 LocalizableMessage message = ERR_FILEPERM_INVALID_UNIX_MODE_STRING.get(modeString); 576 throw new DirectoryException(ResultCode.OTHER, message); 577 } 578 579 switch (modeString.charAt(1)) 580 { 581 case '0': 582 break; 583 case '1': 584 encodedPermission |= GROUP_EXECUTABLE; 585 break; 586 case '2': 587 encodedPermission |= GROUP_WRITABLE; 588 break; 589 case '3': 590 encodedPermission |= GROUP_WRITABLE | GROUP_EXECUTABLE; 591 break; 592 case '4': 593 encodedPermission |= GROUP_READABLE; 594 break; 595 case '5': 596 encodedPermission |= GROUP_READABLE | GROUP_EXECUTABLE; 597 break; 598 case '6': 599 encodedPermission |= GROUP_READABLE | GROUP_WRITABLE; 600 break; 601 case '7': 602 encodedPermission |= GROUP_READABLE | GROUP_WRITABLE | 603 GROUP_EXECUTABLE; 604 break; 605 default: 606 LocalizableMessage message = ERR_FILEPERM_INVALID_UNIX_MODE_STRING.get(modeString); 607 throw new DirectoryException(ResultCode.OTHER, message); 608 } 609 610 switch (modeString.charAt(2)) 611 { 612 case '0': 613 break; 614 case '1': 615 encodedPermission |= OTHER_EXECUTABLE; 616 break; 617 case '2': 618 encodedPermission |= OTHER_WRITABLE; 619 break; 620 case '3': 621 encodedPermission |= OTHER_WRITABLE | OTHER_EXECUTABLE; 622 break; 623 case '4': 624 encodedPermission |= OTHER_READABLE; 625 break; 626 case '5': 627 encodedPermission |= OTHER_READABLE | OTHER_EXECUTABLE; 628 break; 629 case '6': 630 encodedPermission |= OTHER_READABLE | OTHER_WRITABLE; 631 break; 632 case '7': 633 encodedPermission |= OTHER_READABLE | OTHER_WRITABLE | 634 OTHER_EXECUTABLE; 635 break; 636 default: 637 LocalizableMessage message = ERR_FILEPERM_INVALID_UNIX_MODE_STRING.get(modeString); 638 throw new DirectoryException(ResultCode.OTHER, message); 639 } 640 641 return new FilePermission(encodedPermission); 642 } 643 644 645 646 /** 647 * Build a file permissions string in the "rwx" form expected by NIO, 648 * but with optional prefix strings before each three character block. 649 * <p> 650 * For example: "rwxr-xrw-" and "Owner=rwx, Group=r-x", Other=rw-". 651 * 652 * @param p The file permissions to use. 653 * @param buffer The buffer being appended to. 654 * @param owner The owner prefix, must not be null. 655 * @param group The group prefix, must not be null. 656 * @param other The other prefix, must not be null. 657 */ 658 private static void toPOSIXString(FilePermission p, StringBuilder buffer, 659 String owner, String group, String other) 660 { 661 buffer.append(owner); 662 buffer.append(p.isOwnerReadable() ? "r" : "-"); 663 buffer.append(p.isOwnerWritable() ? "w" : "-"); 664 buffer.append(p.isOwnerExecutable() ? "x" : "-"); 665 666 buffer.append(group); 667 buffer.append(p.isGroupReadable() ? "r" : "-"); 668 buffer.append(p.isGroupWritable() ? "w" : "-"); 669 buffer.append(p.isGroupExecutable() ? "x" : "-"); 670 671 buffer.append(other); 672 buffer.append(p.isOtherReadable() ? "r" : "-"); 673 buffer.append(p.isOtherWritable() ? "w" : "-"); 674 buffer.append(p.isOtherExecutable() ? "x" : "-"); 675 } 676 677 678 679 /** 680 * Retrieves a string representation of this file permission. 681 * 682 * @return A string representation of this file permission. 683 */ 684 @Override 685 public String toString() 686 { 687 StringBuilder buffer = new StringBuilder(); 688 toString(buffer); 689 return buffer.toString(); 690 } 691 692 693 694 /** 695 * Appends a string representation of this file permission to the 696 * given buffer. 697 * 698 * @param buffer The buffer to which the data should be appended. 699 */ 700 public void toString(StringBuilder buffer) 701 { 702 toPOSIXString(this, buffer, "Owner=", ", Group=", ", Other="); 703 } 704} 705