001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.compress.archivers.cpio; 020 021import java.io.File; 022import java.util.Date; 023 024import org.apache.commons.compress.archivers.ArchiveEntry; 025 026/** 027 * A cpio archive consists of a sequence of files. There are several types of 028 * headers defided in two categories of new and old format. The headers are 029 * recognized by magic numbers: 030 * 031 * <ul> 032 * <li>"070701" ASCII for new portable format</li> 033 * <li>"070702" ASCII for new portable format with CRC</li> 034 * <li>"070707" ASCII for old ascii (also known as Portable ASCII, odc or old 035 * character format</li> 036 * <li>070707 binary for old binary</li> 037 * </ul> 038 * 039 * <p>The old binary format is limited to 16 bits for user id, group 040 * id, device, and inode numbers. It is limited to 4 gigabyte file 041 * sizes. 042 * 043 * The old ASCII format is limited to 18 bits for the user id, group 044 * id, device, and inode numbers. It is limited to 8 gigabyte file 045 * sizes. 046 * 047 * The new ASCII format is limited to 4 gigabyte file sizes. 048 * 049 * CPIO 2.5 knows also about tar, but it is not recognized here.</p> 050 * 051 * 052 * <h3>OLD FORMAT</h3> 053 * 054 * <p>Each file has a 76 (ascii) / 26 (binary) byte header, a variable 055 * length, NUL terminated filename, and variable length file data. A 056 * header for a filename "TRAILER!!!" indicates the end of the 057 * archive.</p> 058 * 059 * <p>All the fields in the header are ISO 646 (approximately ASCII) 060 * strings of octal numbers, left padded, not NUL terminated.</p> 061 * 062 * <pre> 063 * FIELDNAME NOTES 064 * c_magic The integer value octal 070707. This value can be used to deter- 065 * mine whether this archive is written with little-endian or big- 066 * endian integers. 067 * c_dev Device that contains a directory entry for this file 068 * c_ino I-node number that identifies the input file to the file system 069 * c_mode The mode specifies both the regular permissions and the file type. 070 * c_uid Numeric User ID of the owner of the input file 071 * c_gid Numeric Group ID of the owner of the input file 072 * c_nlink Number of links that are connected to the input file 073 * c_rdev For block special and character special entries, this field 074 * contains the associated device number. For all other entry types, 075 * it should be set to zero by writers and ignored by readers. 076 * c_mtime[2] Modification time of the file, indicated as the number of seconds 077 * since the start of the epoch, 00:00:00 UTC January 1, 1970. The 078 * four-byte integer is stored with the most-significant 16 bits 079 * first followed by the least-significant 16 bits. Each of the two 080 * 16 bit values are stored in machine-native byte order. 081 * c_namesize Length of the path name, including the terminating null byte 082 * c_filesize[2] Length of the file in bytes. This is the length of the data 083 * section that follows the header structure. Must be 0 for 084 * FIFOs and directories 085 * 086 * All fields are unsigned short fields with 16-bit integer values 087 * apart from c_mtime and c_filesize which are 32-bit integer values 088 * </pre> 089 * 090 * <p>If necessary, the filename and file data are padded with a NUL byte to an even length</p> 091 * 092 * <p>Special files, directories, and the trailer are recorded with 093 * the h_filesize field equal to 0.</p> 094 * 095 * <p>In the ASCII version of this format, the 16-bit entries are represented as 6-byte octal numbers, 096 * and the 32-bit entries are represented as 11-byte octal numbers. No padding is added.</p> 097 * 098 * <h3>NEW FORMAT</h3> 099 * 100 * <p>Each file has a 110 byte header, a variable length, NUL 101 * terminated filename, and variable length file data. A header for a 102 * filename "TRAILER!!!" indicates the end of the archive. All the 103 * fields in the header are ISO 646 (approximately ASCII) strings of 104 * hexadecimal numbers, left padded, not NUL terminated.</p> 105 * 106 * <pre> 107 * FIELDNAME NOTES 108 * c_magic[6] The string 070701 for new ASCII, the string 070702 for new ASCII with CRC 109 * c_ino[8] 110 * c_mode[8] 111 * c_uid[8] 112 * c_gid[8] 113 * c_nlink[8] 114 * c_mtim[8] 115 * c_filesize[8] must be 0 for FIFOs and directories 116 * c_maj[8] 117 * c_min[8] 118 * c_rmaj[8] only valid for chr and blk special files 119 * c_rmin[8] only valid for chr and blk special files 120 * c_namesize[8] count includes terminating NUL in pathname 121 * c_check[8] 0 for "new" portable format; for CRC format 122 * the sum of all the bytes in the file 123 * </pre> 124 * 125 * <p>New ASCII Format The "new" ASCII format uses 8-byte hexadecimal 126 * fields for all numbers and separates device numbers into separate 127 * fields for major and minor numbers.</p> 128 * 129 * <p>The pathname is followed by NUL bytes so that the total size of 130 * the fixed header plus pathname is a multiple of four. Likewise, the 131 * file data is padded to a multiple of four bytes.</p> 132 * 133 * <p>This class uses mutable fields and is not considered to be 134 * threadsafe.</p> 135 * 136 * <p>Based on code from the jRPM project (http://jrpm.sourceforge.net).</p> 137 * 138 * <p>The MAGIC numbers and other constants are defined in {@link CpioConstants}</p> 139 * 140 * <p> 141 * N.B. does not handle the cpio "tar" format 142 * </p> 143 * @NotThreadSafe 144 * @see <a href="http://people.freebsd.org/~kientzle/libarchive/man/cpio.5.txt">http://people.freebsd.org/~kientzle/libarchive/man/cpio.5.txt</a> 145 */ 146public class CpioArchiveEntry implements CpioConstants, ArchiveEntry { 147 148 // Header description fields - should be same throughout an archive 149 150 /** 151 * See constructor documenation for possible values. 152 */ 153 private final short fileFormat; 154 155 /** The number of bytes in each header record; depends on the file format */ 156 private final int headerSize; 157 158 /** The boundary to which the header and data elements are aligned: 0, 2 or 4 bytes */ 159 private final int alignmentBoundary; 160 161 // Header fields 162 163 private long chksum = 0; 164 165 /** Number of bytes in the file */ 166 private long filesize = 0; 167 168 private long gid = 0; 169 170 private long inode = 0; 171 172 private long maj = 0; 173 174 private long min = 0; 175 176 private long mode = 0; 177 178 private long mtime = 0; 179 180 private String name; 181 182 private long nlink = 0; 183 184 private long rmaj = 0; 185 186 private long rmin = 0; 187 188 private long uid = 0; 189 190 /** 191 * Creates a CPIOArchiveEntry with a specified format. 192 * 193 * @param format 194 * The cpio format for this entry. 195 * <p> 196 * Possible format values are: 197 * <pre> 198 * CpioConstants.FORMAT_NEW 199 * CpioConstants.FORMAT_NEW_CRC 200 * CpioConstants.FORMAT_OLD_BINARY 201 * CpioConstants.FORMAT_OLD_ASCII 202 * </pre> 203 */ 204 public CpioArchiveEntry(final short format) { 205 switch (format) { 206 case FORMAT_NEW: 207 this.headerSize = 110; 208 this.alignmentBoundary = 4; 209 break; 210 case FORMAT_NEW_CRC: 211 this.headerSize = 110; 212 this.alignmentBoundary = 4; 213 break; 214 case FORMAT_OLD_ASCII: 215 this.headerSize = 76; 216 this.alignmentBoundary = 0; 217 break; 218 case FORMAT_OLD_BINARY: 219 this.headerSize = 26; 220 this.alignmentBoundary = 2; 221 break; 222 default: 223 throw new IllegalArgumentException("Unknown header type"); 224 } 225 this.fileFormat = format; 226 } 227 228 /** 229 * Creates a CPIOArchiveEntry with a specified name. The format of 230 * this entry will be the new format. 231 * 232 * @param name 233 * The name of this entry. 234 */ 235 public CpioArchiveEntry(final String name) { 236 this(FORMAT_NEW, name); 237 } 238 239 /** 240 * Creates a CPIOArchiveEntry with a specified name. 241 * 242 * @param format 243 * The cpio format for this entry. 244 * @param name 245 * The name of this entry. 246 * <p> 247 * Possible format values are: 248 * <pre> 249 * CpioConstants.FORMAT_NEW 250 * CpioConstants.FORMAT_NEW_CRC 251 * CpioConstants.FORMAT_OLD_BINARY 252 * CpioConstants.FORMAT_OLD_ASCII 253 * </pre> 254 * 255 * @since 1.1 256 */ 257 public CpioArchiveEntry(final short format, final String name) { 258 this(format); 259 this.name = name; 260 } 261 262 /** 263 * Creates a CPIOArchiveEntry with a specified name. The format of 264 * this entry will be the new format. 265 * 266 * @param name 267 * The name of this entry. 268 * @param size 269 * The size of this entry 270 */ 271 public CpioArchiveEntry(final String name, final long size) { 272 this(name); 273 this.setSize(size); 274 } 275 276 /** 277 * Creates a CPIOArchiveEntry with a specified name. 278 * 279 * @param format 280 * The cpio format for this entry. 281 * @param name 282 * The name of this entry. 283 * @param size 284 * The size of this entry 285 * <p> 286 * Possible format values are: 287 * <pre> 288 * CpioConstants.FORMAT_NEW 289 * CpioConstants.FORMAT_NEW_CRC 290 * CpioConstants.FORMAT_OLD_BINARY 291 * CpioConstants.FORMAT_OLD_ASCII 292 * </pre> 293 * 294 * @since 1.1 295 */ 296 public CpioArchiveEntry(final short format, final String name, 297 final long size) { 298 this(format, name); 299 this.setSize(size); 300 } 301 302 /** 303 * Creates a CPIOArchiveEntry with a specified name for a 304 * specified file. The format of this entry will be the new 305 * format. 306 * 307 * @param inputFile 308 * The file to gather information from. 309 * @param entryName 310 * The name of this entry. 311 */ 312 public CpioArchiveEntry(File inputFile, String entryName) { 313 this(FORMAT_NEW, inputFile, entryName); 314 } 315 316 /** 317 * Creates a CPIOArchiveEntry with a specified name for a 318 * specified file. 319 * 320 * @param format 321 * The cpio format for this entry. 322 * @param inputFile 323 * The file to gather information from. 324 * @param entryName 325 * The name of this entry. 326 * <p> 327 * Possible format values are: 328 * <pre> 329 * CpioConstants.FORMAT_NEW 330 * CpioConstants.FORMAT_NEW_CRC 331 * CpioConstants.FORMAT_OLD_BINARY 332 * CpioConstants.FORMAT_OLD_ASCII 333 * </pre> 334 * 335 * @since 1.1 336 */ 337 public CpioArchiveEntry(final short format, File inputFile, 338 String entryName) { 339 this(format, entryName, inputFile.isFile() ? inputFile.length() : 0); 340 if (inputFile.isDirectory()){ 341 setMode(C_ISDIR); 342 } else if (inputFile.isFile()){ 343 setMode(C_ISREG); 344 } else { 345 throw new IllegalArgumentException("Cannot determine type of file " 346 + inputFile.getName()); 347 } 348 // TODO set other fields as needed 349 setTime(inputFile.lastModified() / 1000); 350 } 351 352 /** 353 * Check if the method is allowed for the defined format. 354 */ 355 private void checkNewFormat() { 356 if ((this.fileFormat & FORMAT_NEW_MASK) == 0) { 357 throw new UnsupportedOperationException(); 358 } 359 } 360 361 /** 362 * Check if the method is allowed for the defined format. 363 */ 364 private void checkOldFormat() { 365 if ((this.fileFormat & FORMAT_OLD_MASK) == 0) { 366 throw new UnsupportedOperationException(); 367 } 368 } 369 370 /** 371 * Get the checksum. 372 * Only supported for the new formats. 373 * 374 * @return Returns the checksum. 375 * @throws UnsupportedOperationException if the format is not a new format 376 */ 377 public long getChksum() { 378 checkNewFormat(); 379 return this.chksum; 380 } 381 382 /** 383 * Get the device id. 384 * 385 * @return Returns the device id. 386 * @throws UnsupportedOperationException 387 * if this method is called for a CPIOArchiveEntry with a new 388 * format. 389 */ 390 public long getDevice() { 391 checkOldFormat(); 392 return this.min; 393 } 394 395 /** 396 * Get the major device id. 397 * 398 * @return Returns the major device id. 399 * @throws UnsupportedOperationException 400 * if this method is called for a CPIOArchiveEntry with an old 401 * format. 402 */ 403 public long getDeviceMaj() { 404 checkNewFormat(); 405 return this.maj; 406 } 407 408 /** 409 * Get the minor device id 410 * 411 * @return Returns the minor device id. 412 * @throws UnsupportedOperationException if format is not a new format 413 */ 414 public long getDeviceMin() { 415 checkNewFormat(); 416 return this.min; 417 } 418 419 /** 420 * Get the filesize. 421 * 422 * @return Returns the filesize. 423 * @see org.apache.commons.compress.archivers.ArchiveEntry#getSize() 424 */ 425 public long getSize() { 426 return this.filesize; 427 } 428 429 /** 430 * Get the format for this entry. 431 * 432 * @return Returns the format. 433 */ 434 public short getFormat() { 435 return this.fileFormat; 436 } 437 438 /** 439 * Get the group id. 440 * 441 * @return Returns the group id. 442 */ 443 public long getGID() { 444 return this.gid; 445 } 446 447 /** 448 * Get the header size for this CPIO format 449 * 450 * @return Returns the header size in bytes. 451 */ 452 public int getHeaderSize() { 453 return this.headerSize; 454 } 455 456 /** 457 * Get the alignment boundary for this CPIO format 458 * 459 * @return Returns the aligment boundary (0, 2, 4) in bytes 460 */ 461 public int getAlignmentBoundary() { 462 return this.alignmentBoundary; 463 } 464 465 /** 466 * Get the number of bytes needed to pad the header to the alignment boundary. 467 * 468 * @return the number of bytes needed to pad the header (0,1,2,3) 469 */ 470 public int getHeaderPadCount(){ 471 if (this.alignmentBoundary == 0) { return 0; } 472 int size = this.headerSize + 1; // Name has terminating null 473 if (name != null) { 474 size += name.length(); 475 } 476 int remain = size % this.alignmentBoundary; 477 if (remain > 0){ 478 return this.alignmentBoundary - remain; 479 } 480 return 0; 481 } 482 483 /** 484 * Get the number of bytes needed to pad the data to the alignment boundary. 485 * 486 * @return the number of bytes needed to pad the data (0,1,2,3) 487 */ 488 public int getDataPadCount(){ 489 if (this.alignmentBoundary == 0) { return 0; } 490 long size = this.filesize; 491 int remain = (int) (size % this.alignmentBoundary); 492 if (remain > 0){ 493 return this.alignmentBoundary - remain; 494 } 495 return 0; 496 } 497 498 /** 499 * Set the inode. 500 * 501 * @return Returns the inode. 502 */ 503 public long getInode() { 504 return this.inode; 505 } 506 507 /** 508 * Get the mode of this entry (e.g. directory, regular file). 509 * 510 * @return Returns the mode. 511 */ 512 public long getMode() { 513 return mode == 0 && !CPIO_TRAILER.equals(name) ? C_ISREG : mode; 514 } 515 516 /** 517 * Get the name. 518 * 519 * @return Returns the name. 520 */ 521 public String getName() { 522 return this.name; 523 } 524 525 /** 526 * Get the number of links. 527 * 528 * @return Returns the number of links. 529 */ 530 public long getNumberOfLinks() { 531 return nlink == 0 ? 532 isDirectory() ? 2 : 1 533 : nlink; 534 } 535 536 /** 537 * Get the remote device id. 538 * 539 * @return Returns the remote device id. 540 * @throws UnsupportedOperationException 541 * if this method is called for a CPIOArchiveEntry with a new 542 * format. 543 */ 544 public long getRemoteDevice() { 545 checkOldFormat(); 546 return this.rmin; 547 } 548 549 /** 550 * Get the remote major device id. 551 * 552 * @return Returns the remote major device id. 553 * @throws UnsupportedOperationException 554 * if this method is called for a CPIOArchiveEntry with an old 555 * format. 556 */ 557 public long getRemoteDeviceMaj() { 558 checkNewFormat(); 559 return this.rmaj; 560 } 561 562 /** 563 * Get the remote minor device id. 564 * 565 * @return Returns the remote minor device id. 566 * @throws UnsupportedOperationException 567 * if this method is called for a CPIOArchiveEntry with an old 568 * format. 569 */ 570 public long getRemoteDeviceMin() { 571 checkNewFormat(); 572 return this.rmin; 573 } 574 575 /** 576 * Get the time in seconds. 577 * 578 * @return Returns the time. 579 */ 580 public long getTime() { 581 return this.mtime; 582 } 583 584 public Date getLastModifiedDate() { 585 return new Date(1000 * getTime()); 586 } 587 588 /** 589 * Get the user id. 590 * 591 * @return Returns the user id. 592 */ 593 public long getUID() { 594 return this.uid; 595 } 596 597 /** 598 * Check if this entry represents a block device. 599 * 600 * @return TRUE if this entry is a block device. 601 */ 602 public boolean isBlockDevice() { 603 return CpioUtil.fileType(mode) == C_ISBLK; 604 } 605 606 /** 607 * Check if this entry represents a character device. 608 * 609 * @return TRUE if this entry is a character device. 610 */ 611 public boolean isCharacterDevice() { 612 return CpioUtil.fileType(mode) == C_ISCHR; 613 } 614 615 /** 616 * Check if this entry represents a directory. 617 * 618 * @return TRUE if this entry is a directory. 619 */ 620 public boolean isDirectory() { 621 return CpioUtil.fileType(mode) == C_ISDIR; 622 } 623 624 /** 625 * Check if this entry represents a network device. 626 * 627 * @return TRUE if this entry is a network device. 628 */ 629 public boolean isNetwork() { 630 return CpioUtil.fileType(mode) == C_ISNWK; 631 } 632 633 /** 634 * Check if this entry represents a pipe. 635 * 636 * @return TRUE if this entry is a pipe. 637 */ 638 public boolean isPipe() { 639 return CpioUtil.fileType(mode) == C_ISFIFO; 640 } 641 642 /** 643 * Check if this entry represents a regular file. 644 * 645 * @return TRUE if this entry is a regular file. 646 */ 647 public boolean isRegularFile() { 648 return CpioUtil.fileType(mode) == C_ISREG; 649 } 650 651 /** 652 * Check if this entry represents a socket. 653 * 654 * @return TRUE if this entry is a socket. 655 */ 656 public boolean isSocket() { 657 return CpioUtil.fileType(mode) == C_ISSOCK; 658 } 659 660 /** 661 * Check if this entry represents a symbolic link. 662 * 663 * @return TRUE if this entry is a symbolic link. 664 */ 665 public boolean isSymbolicLink() { 666 return CpioUtil.fileType(mode) == C_ISLNK; 667 } 668 669 /** 670 * Set the checksum. The checksum is calculated by adding all bytes of a 671 * file to transfer (crc += buf[pos] & 0xFF). 672 * 673 * @param chksum 674 * The checksum to set. 675 */ 676 public void setChksum(final long chksum) { 677 checkNewFormat(); 678 this.chksum = chksum; 679 } 680 681 /** 682 * Set the device id. 683 * 684 * @param device 685 * The device id to set. 686 * @throws UnsupportedOperationException 687 * if this method is called for a CPIOArchiveEntry with a new 688 * format. 689 */ 690 public void setDevice(final long device) { 691 checkOldFormat(); 692 this.min = device; 693 } 694 695 /** 696 * Set major device id. 697 * 698 * @param maj 699 * The major device id to set. 700 */ 701 public void setDeviceMaj(final long maj) { 702 checkNewFormat(); 703 this.maj = maj; 704 } 705 706 /** 707 * Set the minor device id 708 * 709 * @param min 710 * The minor device id to set. 711 */ 712 public void setDeviceMin(final long min) { 713 checkNewFormat(); 714 this.min = min; 715 } 716 717 /** 718 * Set the filesize. 719 * 720 * @param size 721 * The filesize to set. 722 */ 723 public void setSize(final long size) { 724 if (size < 0 || size > 0xFFFFFFFFL) { 725 throw new IllegalArgumentException("invalid entry size <" + size 726 + ">"); 727 } 728 this.filesize = size; 729 } 730 731 /** 732 * Set the group id. 733 * 734 * @param gid 735 * The group id to set. 736 */ 737 public void setGID(final long gid) { 738 this.gid = gid; 739 } 740 741 /** 742 * Set the inode. 743 * 744 * @param inode 745 * The inode to set. 746 */ 747 public void setInode(final long inode) { 748 this.inode = inode; 749 } 750 751 /** 752 * Set the mode of this entry (e.g. directory, regular file). 753 * 754 * @param mode 755 * The mode to set. 756 */ 757 public void setMode(final long mode) { 758 final long maskedMode = mode & S_IFMT; 759 switch ((int) maskedMode) { 760 case C_ISDIR: 761 case C_ISLNK: 762 case C_ISREG: 763 case C_ISFIFO: 764 case C_ISCHR: 765 case C_ISBLK: 766 case C_ISSOCK: 767 case C_ISNWK: 768 break; 769 default: 770 throw new IllegalArgumentException( 771 "Unknown mode. " 772 + "Full: " + Long.toHexString(mode) 773 + " Masked: " + Long.toHexString(maskedMode)); 774 } 775 776 this.mode = mode; 777 } 778 779 /** 780 * Set the name. 781 * 782 * @param name 783 * The name to set. 784 */ 785 public void setName(final String name) { 786 this.name = name; 787 } 788 789 /** 790 * Set the number of links. 791 * 792 * @param nlink 793 * The number of links to set. 794 */ 795 public void setNumberOfLinks(final long nlink) { 796 this.nlink = nlink; 797 } 798 799 /** 800 * Set the remote device id. 801 * 802 * @param device 803 * The remote device id to set. 804 * @throws UnsupportedOperationException 805 * if this method is called for a CPIOArchiveEntry with a new 806 * format. 807 */ 808 public void setRemoteDevice(final long device) { 809 checkOldFormat(); 810 this.rmin = device; 811 } 812 813 /** 814 * Set the remote major device id. 815 * 816 * @param rmaj 817 * The remote major device id to set. 818 * @throws UnsupportedOperationException 819 * if this method is called for a CPIOArchiveEntry with an old 820 * format. 821 */ 822 public void setRemoteDeviceMaj(final long rmaj) { 823 checkNewFormat(); 824 this.rmaj = rmaj; 825 } 826 827 /** 828 * Set the remote minor device id. 829 * 830 * @param rmin 831 * The remote minor device id to set. 832 * @throws UnsupportedOperationException 833 * if this method is called for a CPIOArchiveEntry with an old 834 * format. 835 */ 836 public void setRemoteDeviceMin(final long rmin) { 837 checkNewFormat(); 838 this.rmin = rmin; 839 } 840 841 /** 842 * Set the time in seconds. 843 * 844 * @param time 845 * The time to set. 846 */ 847 public void setTime(final long time) { 848 this.mtime = time; 849 } 850 851 /** 852 * Set the user id. 853 * 854 * @param uid 855 * The user id to set. 856 */ 857 public void setUID(final long uid) { 858 this.uid = uid; 859 } 860 861 /* (non-Javadoc) 862 * @see java.lang.Object#hashCode() 863 */ 864 @Override 865 public int hashCode() { 866 final int prime = 31; 867 int result = 1; 868 result = prime * result + (name == null ? 0 : name.hashCode()); 869 return result; 870 } 871 872 /* (non-Javadoc) 873 * @see java.lang.Object#equals(java.lang.Object) 874 */ 875 @Override 876 public boolean equals(Object obj) { 877 if (this == obj) { 878 return true; 879 } 880 if (obj == null || getClass() != obj.getClass()) { 881 return false; 882 } 883 CpioArchiveEntry other = (CpioArchiveEntry) obj; 884 if (name == null) { 885 if (other.name != null) { 886 return false; 887 } 888 } else if (!name.equals(other.name)) { 889 return false; 890 } 891 return true; 892 } 893}