github.com/s1s1ty/go@v0.0.0-20180207192209-104445e3140f/src/syscall/fs_nacl.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // A simulated Unix-like file system for use within NaCl. 6 // 7 // The simulation is not particularly tied to NaCl other than the reuse 8 // of NaCl's definition for the Stat_t structure. 9 // 10 // The file system need never be written to disk, so it is represented as 11 // in-memory Go data structures, never in a serialized form. 12 // 13 // TODO: Perhaps support symlinks, although they muck everything up. 14 15 package syscall 16 17 import ( 18 "io" 19 "sync" 20 "unsafe" 21 ) 22 23 // Provided by package runtime. 24 func now() (sec int64, nsec int32) 25 26 // An fsys is a file system. 27 // Since there is no I/O (everything is in memory), 28 // the global lock mu protects the whole file system state, 29 // and that's okay. 30 type fsys struct { 31 mu sync.Mutex 32 root *inode // root directory 33 cwd *inode // process current directory 34 inum uint64 // number of inodes created 35 dev []func() (devFile, error) // table for opening devices 36 } 37 38 // A devFile is the implementation required of device files 39 // like /dev/null or /dev/random. 40 type devFile interface { 41 pread([]byte, int64) (int, error) 42 pwrite([]byte, int64) (int, error) 43 } 44 45 // An inode is a (possibly special) file in the file system. 46 type inode struct { 47 Stat_t 48 data []byte 49 dir []dirent 50 } 51 52 // A dirent describes a single directory entry. 53 type dirent struct { 54 name string 55 inode *inode 56 } 57 58 // An fsysFile is the fileImpl implementation backed by the file system. 59 type fsysFile struct { 60 defaultFileImpl 61 fsys *fsys 62 inode *inode 63 openmode int 64 offset int64 65 dev devFile 66 } 67 68 // newFsys creates a new file system. 69 func newFsys() *fsys { 70 fs := &fsys{} 71 fs.mu.Lock() 72 defer fs.mu.Unlock() 73 ip := fs.newInode() 74 ip.Mode = 0555 | S_IFDIR 75 fs.dirlink(ip, ".", ip) 76 fs.dirlink(ip, "..", ip) 77 fs.cwd = ip 78 fs.root = ip 79 return fs 80 } 81 82 var fs = newFsys() 83 var fsinit = func() {} 84 85 func init() { 86 // do not trigger loading of zipped file system here 87 oldFsinit := fsinit 88 defer func() { fsinit = oldFsinit }() 89 fsinit = func() {} 90 Mkdir("/dev", 0555) 91 Mkdir("/tmp", 0777) 92 mkdev("/dev/null", 0666, openNull) 93 mkdev("/dev/random", 0444, openRandom) 94 mkdev("/dev/urandom", 0444, openRandom) 95 mkdev("/dev/zero", 0666, openZero) 96 chdirEnv() 97 } 98 99 func chdirEnv() { 100 pwd, ok := Getenv("NACLPWD") 101 if ok { 102 chdir(pwd) 103 } 104 } 105 106 // Except where indicated otherwise, unexported methods on fsys 107 // expect fs.mu to have been locked by the caller. 108 109 // newInode creates a new inode. 110 func (fs *fsys) newInode() *inode { 111 fs.inum++ 112 ip := &inode{ 113 Stat_t: Stat_t{ 114 Ino: fs.inum, 115 Blksize: 512, 116 }, 117 } 118 return ip 119 } 120 121 // atime sets ip.Atime to the current time. 122 func (fs *fsys) atime(ip *inode) { 123 sec, nsec := now() 124 ip.Atime, ip.AtimeNsec = sec, int64(nsec) 125 } 126 127 // mtime sets ip.Mtime to the current time. 128 func (fs *fsys) mtime(ip *inode) { 129 sec, nsec := now() 130 ip.Mtime, ip.MtimeNsec = sec, int64(nsec) 131 } 132 133 // dirlookup looks for an entry in the directory dp with the given name. 134 // It returns the directory entry and its index within the directory. 135 func (fs *fsys) dirlookup(dp *inode, name string) (de *dirent, index int, err error) { 136 fs.atime(dp) 137 for i := range dp.dir { 138 de := &dp.dir[i] 139 if de.name == name { 140 fs.atime(de.inode) 141 return de, i, nil 142 } 143 } 144 return nil, 0, ENOENT 145 } 146 147 // dirlink adds to the directory dp an entry for name pointing at the inode ip. 148 // If dp already contains an entry for name, that entry is overwritten. 149 func (fs *fsys) dirlink(dp *inode, name string, ip *inode) { 150 fs.mtime(dp) 151 fs.atime(ip) 152 ip.Nlink++ 153 for i := range dp.dir { 154 if dp.dir[i].name == name { 155 dp.dir[i] = dirent{name, ip} 156 return 157 } 158 } 159 dp.dir = append(dp.dir, dirent{name, ip}) 160 dp.dirSize() 161 } 162 163 func (dp *inode) dirSize() { 164 dp.Size = int64(len(dp.dir)) * (8 + 8 + 2 + 256) // Dirent 165 } 166 167 // skipelem splits path into the first element and the remainder. 168 // the returned first element contains no slashes, and the returned 169 // remainder does not begin with a slash. 170 func skipelem(path string) (elem, rest string) { 171 for len(path) > 0 && path[0] == '/' { 172 path = path[1:] 173 } 174 if len(path) == 0 { 175 return "", "" 176 } 177 i := 0 178 for i < len(path) && path[i] != '/' { 179 i++ 180 } 181 elem, path = path[:i], path[i:] 182 for len(path) > 0 && path[0] == '/' { 183 path = path[1:] 184 } 185 return elem, path 186 } 187 188 // namei translates a file system path name into an inode. 189 // If parent is false, the returned ip corresponds to the given name, and elem is the empty string. 190 // If parent is true, the walk stops at the next-to-last element in the name, 191 // so that ip is the parent directory and elem is the final element in the path. 192 func (fs *fsys) namei(path string, parent bool) (ip *inode, elem string, err error) { 193 // Reject NUL in name. 194 for i := 0; i < len(path); i++ { 195 if path[i] == '\x00' { 196 return nil, "", EINVAL 197 } 198 } 199 200 // Reject empty name. 201 if path == "" { 202 return nil, "", EINVAL 203 } 204 205 if path[0] == '/' { 206 ip = fs.root 207 } else { 208 ip = fs.cwd 209 } 210 211 for len(path) > 0 && path[len(path)-1] == '/' { 212 path = path[:len(path)-1] 213 } 214 215 for { 216 elem, rest := skipelem(path) 217 if elem == "" { 218 if parent && ip.Mode&S_IFMT == S_IFDIR { 219 return ip, ".", nil 220 } 221 break 222 } 223 if ip.Mode&S_IFMT != S_IFDIR { 224 return nil, "", ENOTDIR 225 } 226 if len(elem) >= 256 { 227 return nil, "", ENAMETOOLONG 228 } 229 if parent && rest == "" { 230 // Stop one level early. 231 return ip, elem, nil 232 } 233 de, _, err := fs.dirlookup(ip, elem) 234 if err != nil { 235 return nil, "", err 236 } 237 ip = de.inode 238 path = rest 239 } 240 if parent { 241 return nil, "", ENOTDIR 242 } 243 return ip, "", nil 244 } 245 246 // open opens or creates a file with the given name, open mode, 247 // and permission mode bits. 248 func (fs *fsys) open(name string, openmode int, mode uint32) (fileImpl, error) { 249 dp, elem, err := fs.namei(name, true) 250 if err != nil { 251 return nil, err 252 } 253 var ( 254 ip *inode 255 dev devFile 256 ) 257 de, _, err := fs.dirlookup(dp, elem) 258 if err != nil { 259 if openmode&O_CREATE == 0 { 260 return nil, err 261 } 262 ip = fs.newInode() 263 ip.Mode = mode 264 fs.dirlink(dp, elem, ip) 265 if ip.Mode&S_IFMT == S_IFDIR { 266 fs.dirlink(ip, ".", ip) 267 fs.dirlink(ip, "..", dp) 268 } 269 } else { 270 ip = de.inode 271 if openmode&(O_CREATE|O_EXCL) == O_CREATE|O_EXCL { 272 return nil, EEXIST 273 } 274 if openmode&O_TRUNC != 0 { 275 if ip.Mode&S_IFMT == S_IFDIR { 276 return nil, EISDIR 277 } 278 ip.data = nil 279 } 280 if ip.Mode&S_IFMT == S_IFCHR { 281 if ip.Rdev < 0 || ip.Rdev >= int64(len(fs.dev)) || fs.dev[ip.Rdev] == nil { 282 return nil, ENODEV 283 } 284 dev, err = fs.dev[ip.Rdev]() 285 if err != nil { 286 return nil, err 287 } 288 } 289 } 290 291 switch openmode & O_ACCMODE { 292 case O_WRONLY, O_RDWR: 293 if ip.Mode&S_IFMT == S_IFDIR { 294 return nil, EISDIR 295 } 296 } 297 298 switch ip.Mode & S_IFMT { 299 case S_IFDIR: 300 if openmode&O_ACCMODE != O_RDONLY { 301 return nil, EISDIR 302 } 303 304 case S_IFREG: 305 // ok 306 307 case S_IFCHR: 308 // handled above 309 310 default: 311 // TODO: some kind of special file 312 return nil, EPERM 313 } 314 315 f := &fsysFile{ 316 fsys: fs, 317 inode: ip, 318 openmode: openmode, 319 dev: dev, 320 } 321 if openmode&O_APPEND != 0 { 322 f.offset = ip.Size 323 } 324 return f, nil 325 } 326 327 // fsysFile methods to implement fileImpl. 328 329 func (f *fsysFile) stat(st *Stat_t) error { 330 f.fsys.mu.Lock() 331 defer f.fsys.mu.Unlock() 332 *st = f.inode.Stat_t 333 return nil 334 } 335 336 func (f *fsysFile) read(b []byte) (int, error) { 337 f.fsys.mu.Lock() 338 defer f.fsys.mu.Unlock() 339 n, err := f.preadLocked(b, f.offset) 340 f.offset += int64(n) 341 return n, err 342 } 343 344 func ReadDirent(fd int, buf []byte) (int, error) { 345 f, err := fdToFsysFile(fd) 346 if err != nil { 347 return 0, err 348 } 349 f.fsys.mu.Lock() 350 defer f.fsys.mu.Unlock() 351 if f.inode.Mode&S_IFMT != S_IFDIR { 352 return 0, EINVAL 353 } 354 n, err := f.preadLocked(buf, f.offset) 355 f.offset += int64(n) 356 return n, err 357 } 358 359 func (f *fsysFile) write(b []byte) (int, error) { 360 f.fsys.mu.Lock() 361 defer f.fsys.mu.Unlock() 362 n, err := f.pwriteLocked(b, f.offset) 363 f.offset += int64(n) 364 return n, err 365 } 366 367 func (f *fsysFile) seek(offset int64, whence int) (int64, error) { 368 f.fsys.mu.Lock() 369 defer f.fsys.mu.Unlock() 370 switch whence { 371 case io.SeekCurrent: 372 offset += f.offset 373 case io.SeekEnd: 374 offset += f.inode.Size 375 } 376 if offset < 0 { 377 return 0, EINVAL 378 } 379 if offset > f.inode.Size { 380 return 0, EINVAL 381 } 382 f.offset = offset 383 return offset, nil 384 } 385 386 func (f *fsysFile) pread(b []byte, offset int64) (int, error) { 387 f.fsys.mu.Lock() 388 defer f.fsys.mu.Unlock() 389 return f.preadLocked(b, offset) 390 } 391 392 func (f *fsysFile) pwrite(b []byte, offset int64) (int, error) { 393 f.fsys.mu.Lock() 394 defer f.fsys.mu.Unlock() 395 return f.pwriteLocked(b, offset) 396 } 397 398 func (f *fsysFile) preadLocked(b []byte, offset int64) (int, error) { 399 if f.openmode&O_ACCMODE == O_WRONLY { 400 return 0, EINVAL 401 } 402 if offset < 0 { 403 return 0, EINVAL 404 } 405 if f.dev != nil { 406 f.fsys.atime(f.inode) 407 f.fsys.mu.Unlock() 408 defer f.fsys.mu.Lock() 409 return f.dev.pread(b, offset) 410 } 411 if offset > f.inode.Size { 412 return 0, nil 413 } 414 if int64(len(b)) > f.inode.Size-offset { 415 b = b[:f.inode.Size-offset] 416 } 417 418 if f.inode.Mode&S_IFMT == S_IFDIR { 419 if offset%direntSize != 0 || len(b) != 0 && len(b) < direntSize { 420 return 0, EINVAL 421 } 422 fs.atime(f.inode) 423 n := 0 424 for len(b) >= direntSize { 425 src := f.inode.dir[int(offset/direntSize)] 426 dst := (*Dirent)(unsafe.Pointer(&b[0])) 427 dst.Ino = int64(src.inode.Ino) 428 dst.Off = offset 429 dst.Reclen = direntSize 430 for i := range dst.Name { 431 dst.Name[i] = 0 432 } 433 copy(dst.Name[:], src.name) 434 n += direntSize 435 offset += direntSize 436 b = b[direntSize:] 437 } 438 return n, nil 439 } 440 441 fs.atime(f.inode) 442 n := copy(b, f.inode.data[offset:]) 443 return n, nil 444 } 445 446 func (f *fsysFile) pwriteLocked(b []byte, offset int64) (int, error) { 447 if f.openmode&O_ACCMODE == O_RDONLY { 448 return 0, EINVAL 449 } 450 if offset < 0 { 451 return 0, EINVAL 452 } 453 if f.dev != nil { 454 f.fsys.atime(f.inode) 455 f.fsys.mu.Unlock() 456 defer f.fsys.mu.Lock() 457 return f.dev.pwrite(b, offset) 458 } 459 if offset > f.inode.Size { 460 return 0, EINVAL 461 } 462 f.fsys.mtime(f.inode) 463 n := copy(f.inode.data[offset:], b) 464 if n < len(b) { 465 f.inode.data = append(f.inode.data, b[n:]...) 466 f.inode.Size = int64(len(f.inode.data)) 467 } 468 return len(b), nil 469 } 470 471 // Standard Unix system calls. 472 473 func Open(path string, openmode int, perm uint32) (fd int, err error) { 474 fsinit() 475 fs.mu.Lock() 476 defer fs.mu.Unlock() 477 f, err := fs.open(path, openmode, perm&0777|S_IFREG) 478 if err != nil { 479 return -1, err 480 } 481 return newFD(f), nil 482 } 483 484 func Mkdir(path string, perm uint32) error { 485 fs.mu.Lock() 486 defer fs.mu.Unlock() 487 _, err := fs.open(path, O_CREATE|O_EXCL, perm&0777|S_IFDIR) 488 return err 489 } 490 491 func Getcwd(buf []byte) (n int, err error) { 492 // Force package os to default to the old algorithm using .. and directory reads. 493 return 0, ENOSYS 494 } 495 496 func Stat(path string, st *Stat_t) error { 497 fsinit() 498 fs.mu.Lock() 499 defer fs.mu.Unlock() 500 ip, _, err := fs.namei(path, false) 501 if err != nil { 502 return err 503 } 504 *st = ip.Stat_t 505 return nil 506 } 507 508 func Lstat(path string, st *Stat_t) error { 509 return Stat(path, st) 510 } 511 512 func unlink(path string, isdir bool) error { 513 fsinit() 514 fs.mu.Lock() 515 defer fs.mu.Unlock() 516 dp, elem, err := fs.namei(path, true) 517 if err != nil { 518 return err 519 } 520 if elem == "." || elem == ".." { 521 return EINVAL 522 } 523 de, _, err := fs.dirlookup(dp, elem) 524 if err != nil { 525 return err 526 } 527 if isdir { 528 if de.inode.Mode&S_IFMT != S_IFDIR { 529 return ENOTDIR 530 } 531 if len(de.inode.dir) != 2 { 532 return ENOTEMPTY 533 } 534 } else { 535 if de.inode.Mode&S_IFMT == S_IFDIR { 536 return EISDIR 537 } 538 } 539 de.inode.Nlink-- 540 *de = dp.dir[len(dp.dir)-1] 541 dp.dir = dp.dir[:len(dp.dir)-1] 542 dp.dirSize() 543 return nil 544 } 545 546 func Unlink(path string) error { 547 return unlink(path, false) 548 } 549 550 func Rmdir(path string) error { 551 return unlink(path, true) 552 } 553 554 func Chmod(path string, mode uint32) error { 555 fsinit() 556 fs.mu.Lock() 557 defer fs.mu.Unlock() 558 ip, _, err := fs.namei(path, false) 559 if err != nil { 560 return err 561 } 562 ip.Mode = ip.Mode&^0777 | mode&0777 563 return nil 564 } 565 566 func Fchmod(fd int, mode uint32) error { 567 f, err := fdToFsysFile(fd) 568 if err != nil { 569 return err 570 } 571 f.fsys.mu.Lock() 572 defer f.fsys.mu.Unlock() 573 f.inode.Mode = f.inode.Mode&^0777 | mode&0777 574 return nil 575 } 576 577 func Chown(path string, uid, gid int) error { 578 fsinit() 579 fs.mu.Lock() 580 defer fs.mu.Unlock() 581 ip, _, err := fs.namei(path, false) 582 if err != nil { 583 return err 584 } 585 ip.Uid = uint32(uid) 586 ip.Gid = uint32(gid) 587 return nil 588 } 589 590 func Fchown(fd int, uid, gid int) error { 591 fs.mu.Lock() 592 defer fs.mu.Unlock() 593 f, err := fdToFsysFile(fd) 594 if err != nil { 595 return err 596 } 597 f.fsys.mu.Lock() 598 defer f.fsys.mu.Unlock() 599 f.inode.Uid = uint32(uid) 600 f.inode.Gid = uint32(gid) 601 return nil 602 } 603 604 func Lchown(path string, uid, gid int) error { 605 return Chown(path, uid, gid) 606 } 607 608 func UtimesNano(path string, ts []Timespec) error { 609 if len(ts) != 2 { 610 return EINVAL 611 } 612 fsinit() 613 fs.mu.Lock() 614 defer fs.mu.Unlock() 615 ip, _, err := fs.namei(path, false) 616 if err != nil { 617 return err 618 } 619 ip.Atime = ts[0].Sec 620 ip.AtimeNsec = int64(ts[0].Nsec) 621 ip.Mtime = ts[1].Sec 622 ip.MtimeNsec = int64(ts[1].Nsec) 623 return nil 624 } 625 626 func Link(path, link string) error { 627 fsinit() 628 fs.mu.Lock() 629 defer fs.mu.Unlock() 630 ip, _, err := fs.namei(path, false) 631 if err != nil { 632 return err 633 } 634 dp, elem, err := fs.namei(link, true) 635 if err != nil { 636 return err 637 } 638 if ip.Mode&S_IFMT == S_IFDIR { 639 return EPERM 640 } 641 _, _, err = fs.dirlookup(dp, elem) 642 if err == nil { 643 return EEXIST 644 } 645 fs.dirlink(dp, elem, ip) 646 return nil 647 } 648 649 func Rename(from, to string) error { 650 fsinit() 651 fs.mu.Lock() 652 defer fs.mu.Unlock() 653 fdp, felem, err := fs.namei(from, true) 654 if err != nil { 655 return err 656 } 657 fde, _, err := fs.dirlookup(fdp, felem) 658 if err != nil { 659 return err 660 } 661 tdp, telem, err := fs.namei(to, true) 662 if err != nil { 663 return err 664 } 665 fs.dirlink(tdp, telem, fde.inode) 666 fde.inode.Nlink-- 667 *fde = fdp.dir[len(fdp.dir)-1] 668 fdp.dir = fdp.dir[:len(fdp.dir)-1] 669 fdp.dirSize() 670 return nil 671 } 672 673 func (fs *fsys) truncate(ip *inode, length int64) error { 674 if length > 1e9 || ip.Mode&S_IFMT != S_IFREG { 675 return EINVAL 676 } 677 if length < int64(len(ip.data)) { 678 ip.data = ip.data[:length] 679 } else { 680 data := make([]byte, length) 681 copy(data, ip.data) 682 ip.data = data 683 } 684 ip.Size = int64(len(ip.data)) 685 return nil 686 } 687 688 func Truncate(path string, length int64) error { 689 fsinit() 690 fs.mu.Lock() 691 defer fs.mu.Unlock() 692 ip, _, err := fs.namei(path, false) 693 if err != nil { 694 return err 695 } 696 return fs.truncate(ip, length) 697 } 698 699 func Ftruncate(fd int, length int64) error { 700 f, err := fdToFsysFile(fd) 701 if err != nil { 702 return err 703 } 704 f.fsys.mu.Lock() 705 defer f.fsys.mu.Unlock() 706 return f.fsys.truncate(f.inode, length) 707 } 708 709 func Chdir(path string) error { 710 fsinit() 711 return chdir(path) 712 } 713 714 func chdir(path string) error { 715 fs.mu.Lock() 716 defer fs.mu.Unlock() 717 ip, _, err := fs.namei(path, false) 718 if err != nil { 719 return err 720 } 721 fs.cwd = ip 722 return nil 723 } 724 725 func Fchdir(fd int) error { 726 f, err := fdToFsysFile(fd) 727 if err != nil { 728 return err 729 } 730 f.fsys.mu.Lock() 731 defer f.fsys.mu.Unlock() 732 if f.inode.Mode&S_IFMT != S_IFDIR { 733 return ENOTDIR 734 } 735 fs.cwd = f.inode 736 return nil 737 } 738 739 func Readlink(path string, buf []byte) (n int, err error) { 740 return 0, ENOSYS 741 } 742 743 func Symlink(path, link string) error { 744 return ENOSYS 745 } 746 747 func Fsync(fd int) error { 748 return nil 749 } 750 751 // Special devices. 752 753 func mkdev(path string, mode uint32, open func() (devFile, error)) error { 754 f, err := fs.open(path, O_CREATE|O_RDONLY|O_EXCL, S_IFCHR|mode) 755 if err != nil { 756 return err 757 } 758 ip := f.(*fsysFile).inode 759 ip.Rdev = int64(len(fs.dev)) 760 fs.dev = append(fs.dev, open) 761 return nil 762 } 763 764 type nullFile struct{} 765 766 func openNull() (devFile, error) { return &nullFile{}, nil } 767 func (f *nullFile) close() error { return nil } 768 func (f *nullFile) pread(b []byte, offset int64) (int, error) { return 0, nil } 769 func (f *nullFile) pwrite(b []byte, offset int64) (int, error) { return len(b), nil } 770 771 type zeroFile struct{} 772 773 func openZero() (devFile, error) { return &zeroFile{}, nil } 774 func (f *zeroFile) close() error { return nil } 775 func (f *zeroFile) pwrite(b []byte, offset int64) (int, error) { return len(b), nil } 776 777 func (f *zeroFile) pread(b []byte, offset int64) (int, error) { 778 for i := range b { 779 b[i] = 0 780 } 781 return len(b), nil 782 } 783 784 type randomFile struct{} 785 786 func openRandom() (devFile, error) { 787 return randomFile{}, nil 788 } 789 790 func (f randomFile) close() error { 791 return nil 792 } 793 794 func (f randomFile) pread(b []byte, offset int64) (int, error) { 795 if err := naclGetRandomBytes(b); err != nil { 796 return 0, err 797 } 798 return len(b), nil 799 } 800 801 func (f randomFile) pwrite(b []byte, offset int64) (int, error) { 802 return 0, EPERM 803 } 804 805 func fdToFsysFile(fd int) (*fsysFile, error) { 806 f, err := fdToFile(fd) 807 if err != nil { 808 return nil, err 809 } 810 impl := f.impl 811 fsysf, ok := impl.(*fsysFile) 812 if !ok { 813 return nil, EINVAL 814 } 815 return fsysf, nil 816 } 817 818 // create creates a file in the file system with the given name, mode, time, and data. 819 // It is meant to be called when initializing the file system image. 820 func create(name string, mode uint32, sec int64, data []byte) error { 821 fs.mu.Lock() 822 defer fs.mu.Unlock() 823 f, err := fs.open(name, O_CREATE|O_EXCL, mode) 824 if err != nil { 825 if mode&S_IFMT == S_IFDIR { 826 ip, _, err := fs.namei(name, false) 827 if err == nil && (ip.Mode&S_IFMT) == S_IFDIR { 828 return nil // directory already exists 829 } 830 } 831 return err 832 } 833 ip := f.(*fsysFile).inode 834 ip.Atime = sec 835 ip.Mtime = sec 836 ip.Ctime = sec 837 if len(data) > 0 { 838 ip.Size = int64(len(data)) 839 ip.data = data 840 } 841 return nil 842 }