1 /* 2 * Copyright (c) 1989 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Rick Macklem at The University of Guelph. 7 * 8 * Redistribution and use in source and binary forms are permitted 9 * provided that the above copyright notice and this paragraph are 10 * duplicated in all such forms and that any documentation, 11 * advertising materials, and other materials related to such 12 * distribution and use acknowledge that the software was developed 13 * by the University of California, Berkeley. The name of the 14 * University may not be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 19 * 20 * @(#)nfs_serv.c 7.5 (Berkeley) 07/16/89 21 */ 22 23 /* 24 * nfs version 2 server calls to vnode ops 25 * - these routines generally have 3 phases 26 * 1 - break down and validate rpc request in mbuf list 27 * 2 - do the vnode ops for the request 28 * (surprisingly ?? many are very similar to syscalls in vfs_syscalls.c) 29 * 3 - build the rpc reply in an mbuf list 30 * nb: 31 * - do not mix the phases, since the nfsm_?? macros can return failures 32 * on mbuf exhaustion or similar and do not do any vrele() or vput()'s 33 * 34 * - the nfsm_reply() macro generates an nfs rpc reply with the nfs 35 * error number iff error != 0 whereas 36 * nfsm_srverr simply drops the mbufs and gives up 37 * (==> nfsm_srverr implies an error here at the server, usually mbuf 38 * exhaustion) 39 */ 40 41 #include "strings.h" 42 #include "time.h" 43 #include "param.h" 44 #include "mount.h" 45 #include "malloc.h" 46 #include "mbuf.h" 47 #include "file.h" 48 #include "user.h" 49 #include "../ufs/dir.h" 50 #include "vnode.h" 51 #include "uio.h" 52 #include "ucred.h" 53 #include "namei.h" 54 #include "errno.h" 55 #include "../ufs/inode.h" 56 #include "nfsv2.h" 57 #include "nfs.h" 58 #include "xdr_subs.h" 59 #include "nfsm_subs.h" 60 61 /* Defs */ 62 #define TRUE 1 63 #define FALSE 0 64 65 /* Global vars */ 66 extern u_long nfs_procids[NFS_NPROCS]; 67 extern u_long nfs_xdrneg1; 68 extern u_long nfs_false, nfs_true; 69 nfstype nfs_type[VSOCK+1]={ NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFNON, }; 70 71 /* 72 * nfs getattr service 73 */ 74 nfsrv_getattr(mrep, md, dpos, cred, xid, mrq) 75 struct mbuf **mrq; 76 struct mbuf *mrep, *md; 77 caddr_t dpos; 78 struct ucred *cred; 79 u_long xid; 80 { 81 struct vattr va; 82 register struct vattr *vap = &va; 83 struct vnode *vp; 84 nfsv2fh_t nfh; 85 fhandle_t *fhp; 86 nfsm_srvars; 87 88 fhp = &nfh.fh_generic; 89 nfsm_srvmtofh(fhp); 90 if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred)) 91 nfsm_reply(0); 92 error = VOP_GETATTR(vp, vap, cred); 93 vput(vp); 94 nfsm_reply(NFSX_FATTR); 95 nfsm_build(p, u_long *, NFSX_FATTR); 96 *p++ = vtonfs_type(vap->va_type); 97 *p++ = vtonfs_mode(vap->va_type, vap->va_mode); 98 *p++ = txdr_unsigned(vap->va_nlink); 99 *p++ = txdr_unsigned(vap->va_uid); 100 *p++ = txdr_unsigned(vap->va_gid); 101 *p++ = txdr_unsigned(vap->va_size); 102 *p++ = txdr_unsigned(vap->va_blocksize); 103 *p++ = txdr_unsigned(vap->va_rdev); 104 *p++ = txdr_unsigned(vap->va_bytes); 105 *p++ = txdr_unsigned(vap->va_fsid); 106 *p++ = txdr_unsigned(vap->va_fileid); 107 txdr_time(&(vap->va_atime), p); 108 p += 2; 109 txdr_time(&(vap->va_mtime), p); 110 p += 2; 111 txdr_time(&(vap->va_ctime), p); 112 nfsm_srvdone; 113 } 114 115 /* 116 * nfs setattr service 117 */ 118 nfsrv_setattr(mrep, md, dpos, cred, xid, mrq) 119 struct mbuf **mrq; 120 struct mbuf *mrep, *md; 121 caddr_t dpos; 122 struct ucred *cred; 123 u_long xid; 124 { 125 struct vattr va; 126 register struct vattr *vap = &va; 127 struct vnode *vp; 128 nfsv2fh_t nfh; 129 fhandle_t *fhp; 130 nfsm_srvars; 131 132 fhp = &nfh.fh_generic; 133 nfsm_srvmtofh(fhp); 134 nfsm_disect(p, u_long *, NFSX_SATTR); 135 if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred)) 136 nfsm_reply(0); 137 if (error = nfsrv_access(vp, VWRITE, cred)) 138 goto out; 139 vattr_null(vap); 140 /* 141 * Nah nah nah nah na nah 142 * There is a bug in the Sun client that puts 0xffff in the mode 143 * field of sattr when it should put in 0xffffffff. The u_short 144 * doesn't sign extend. 145 * --> check the low order 2 bytes for 0xffff 146 */ 147 if ((fxdr_unsigned(int, *p) & 0xffff) != 0xffff) 148 vap->va_mode = nfstov_mode(*p); 149 if (*++p != nfs_xdrneg1) 150 vap->va_uid = fxdr_unsigned(uid_t, *p); 151 if (*++p != nfs_xdrneg1) 152 vap->va_gid = fxdr_unsigned(gid_t, *p); 153 if (*++p != nfs_xdrneg1) { 154 vap->va_size = fxdr_unsigned(u_long, *p); 155 } 156 if (*++p != nfs_xdrneg1) 157 fxdr_time(p, &(vap->va_atime)); 158 p += 2; 159 if (*p != nfs_xdrneg1) 160 fxdr_time(p, &(vap->va_mtime)); 161 if (error = VOP_SETATTR(vp, vap, cred)) { 162 vput(vp); 163 nfsm_reply(0); 164 } 165 error = VOP_GETATTR(vp, vap, cred); 166 out: 167 vput(vp); 168 nfsm_reply(NFSX_FATTR); 169 nfsm_build(p, u_long *, NFSX_FATTR); 170 *p++ = vtonfs_type(vap->va_type); 171 *p++ = vtonfs_mode(vap->va_type, vap->va_mode); 172 *p++ = txdr_unsigned(vap->va_nlink); 173 *p++ = txdr_unsigned(vap->va_uid); 174 *p++ = txdr_unsigned(vap->va_gid); 175 *p++ = txdr_unsigned(vap->va_size); 176 *p++ = txdr_unsigned(vap->va_blocksize); 177 *p++ = txdr_unsigned(vap->va_rdev); 178 *p++ = txdr_unsigned(vap->va_bytes); 179 *p++ = txdr_unsigned(vap->va_fsid); 180 *p++ = txdr_unsigned(vap->va_fileid); 181 txdr_time(&(vap->va_atime), p); 182 p += 2; 183 txdr_time(&(vap->va_mtime), p); 184 p += 2; 185 txdr_time(&(vap->va_ctime), p); 186 nfsm_srvdone; 187 } 188 189 /* 190 * nfs lookup rpc 191 */ 192 nfsrv_lookup(mrep, md, dpos, cred, xid, mrq) 193 struct mbuf **mrq; 194 struct mbuf *mrep, *md; 195 caddr_t dpos; 196 struct ucred *cred; 197 u_long xid; 198 { 199 register struct nameidata *ndp = &u.u_nd; 200 struct vnode *vp; 201 nfsv2fh_t nfh; 202 fhandle_t *fhp; 203 nfsm_srvars; 204 long len; 205 struct vattr va, *vap = &va; 206 207 fhp = &nfh.fh_generic; 208 nfsm_srvmtofh(fhp); 209 nfsm_srvstrsiz(len, NFS_MAXNAMLEN); 210 ndp->ni_cred = cred; 211 ndp->ni_nameiop = LOOKUP | LOCKLEAF; 212 if (error = nfs_namei(ndp, fhp, len, &md, &dpos)) 213 nfsm_reply(0); 214 vp = ndp->ni_vp; 215 bzero((caddr_t)fhp, sizeof(nfh)); 216 fhp->fh_fsid = vp->v_mount->m_fsid; 217 if (error = VFS_VPTOFH(vp, &fhp->fh_fid)) { 218 vput(vp); 219 nfsm_reply(0); 220 } 221 error = VOP_GETATTR(vp, vap, cred); 222 vput(vp); 223 nfsm_reply(NFSX_FH+NFSX_FATTR); 224 nfsm_srvfhtom(fhp); 225 nfsm_build(p, u_long *, NFSX_FATTR); 226 *p++ = vtonfs_type(vap->va_type); 227 *p++ = vtonfs_mode(vap->va_type, vap->va_mode); 228 *p++ = txdr_unsigned(vap->va_nlink); 229 *p++ = txdr_unsigned(vap->va_uid); 230 *p++ = txdr_unsigned(vap->va_gid); 231 *p++ = txdr_unsigned(vap->va_size); 232 *p++ = txdr_unsigned(vap->va_blocksize); 233 *p++ = txdr_unsigned(vap->va_rdev); 234 *p++ = txdr_unsigned(vap->va_bytes); 235 *p++ = txdr_unsigned(vap->va_fsid); 236 *p++ = txdr_unsigned(vap->va_fileid); 237 txdr_time(&(vap->va_atime), p); 238 p += 2; 239 txdr_time(&(vap->va_mtime), p); 240 p += 2; 241 txdr_time(&(vap->va_ctime), p); 242 nfsm_srvdone; 243 } 244 245 /* 246 * nfs readlink service 247 */ 248 nfsrv_readlink(mrep, md, dpos, cred, xid, mrq) 249 struct mbuf **mrq; 250 struct mbuf *mrep, *md; 251 caddr_t dpos; 252 struct ucred *cred; 253 long xid; 254 { 255 struct iovec iv[NFS_MAXPATHLEN/MLEN+1]; 256 register struct iovec *ivp = iv; 257 register struct mbuf *mp; 258 nfsm_srvars; 259 struct mbuf *mp2, *mp3; 260 struct vnode *vp; 261 nfsv2fh_t nfh; 262 fhandle_t *fhp; 263 struct uio io, *uiop = &io; 264 int i, tlen, len; 265 266 fhp = &nfh.fh_generic; 267 nfsm_srvmtofh(fhp); 268 len = 0; 269 i = 0; 270 while (len < NFS_MAXPATHLEN) { 271 MGET(mp, M_WAIT, MT_DATA); 272 NFSMCLGET(mp, M_WAIT); 273 mp->m_len = NFSMSIZ(mp); 274 if (len == 0) 275 mp3 = mp2 = mp; 276 else 277 mp2->m_next = mp; 278 if ((len+mp->m_len) > NFS_MAXPATHLEN) { 279 mp->m_len = NFS_MAXPATHLEN-len; 280 len = NFS_MAXPATHLEN; 281 } else 282 len += mp->m_len; 283 ivp->iov_base = mtod(mp, caddr_t); 284 ivp->iov_len = mp->m_len; 285 i++; 286 ivp++; 287 } 288 uiop->uio_iov = iv; 289 uiop->uio_iovcnt = i; 290 uiop->uio_offset = 0; 291 uiop->uio_resid = len; 292 uiop->uio_rw = UIO_READ; 293 uiop->uio_segflg = UIO_SYSSPACE; 294 if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred)) { 295 m_freem(mp3); 296 nfsm_reply(0); 297 } 298 if (vp->v_type != VLNK) { 299 error = EINVAL; 300 goto out; 301 } 302 error = VOP_READLINK(vp, uiop, cred); 303 out: 304 vput(vp); 305 if (error) 306 m_freem(mp3); 307 nfsm_reply(NFSX_UNSIGNED); 308 if (uiop->uio_resid > 0) { 309 len -= uiop->uio_resid; 310 tlen = nfsm_rndup(len); 311 nfsm_adj(mp3, NFS_MAXPATHLEN-tlen, tlen-len); 312 } 313 nfsm_build(p, u_long *, NFSX_UNSIGNED); 314 *p = txdr_unsigned(len); 315 mb->m_next = mp3; 316 nfsm_srvdone; 317 } 318 319 /* 320 * nfs read service 321 */ 322 nfsrv_read(mrep, md, dpos, cred, xid, mrq) 323 struct mbuf **mrq; 324 struct mbuf *mrep, *md; 325 caddr_t dpos; 326 struct ucred *cred; 327 u_long xid; 328 { 329 struct iovec iv[NFS_MAXDATA/MCLBYTES+1]; 330 register struct iovec *ivp = iv; 331 register struct mbuf *mp; 332 nfsm_srvars; 333 struct mbuf *mp2, *mp3; 334 struct vnode *vp; 335 nfsv2fh_t nfh; 336 fhandle_t *fhp; 337 struct uio io, *uiop = &io; 338 struct vattr va, *vap = &va; 339 int i, tlen, cnt, len, nio, left; 340 off_t off; 341 342 fhp = &nfh.fh_generic; 343 nfsm_srvmtofh(fhp); 344 nfsm_disect(p, u_long *, NFSX_UNSIGNED); 345 off = fxdr_unsigned(off_t, *p); 346 nfsm_srvstrsiz(cnt, NFS_MAXDATA); 347 if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred)) 348 nfsm_reply(0); 349 if (error = nfsrv_access(vp, VREAD | VEXEC, cred)) { 350 vput(vp); 351 nfsm_reply(0); 352 } 353 len = left = cnt; 354 nio = (cnt+MCLBYTES-1)/MCLBYTES; 355 uiop->uio_iov = ivp; 356 uiop->uio_iovcnt = nio; 357 uiop->uio_offset = off; 358 uiop->uio_resid = cnt; 359 uiop->uio_rw = UIO_READ; 360 uiop->uio_segflg = UIO_SYSSPACE; 361 for (i = 0; i < nio; i++) { 362 MGET(mp, M_WAIT, MT_DATA); 363 if (left > MLEN) 364 NFSMCLGET(mp, M_WAIT); 365 mp->m_len = (M_HASCL(mp)) ? MCLBYTES : MLEN; 366 if (i == 0) { 367 mp3 = mp2 = mp; 368 } else { 369 mp2->m_next = mp; 370 mp2 = mp; 371 } 372 if (left > MLEN && !M_HASCL(mp)) { 373 m_freem(mp3); 374 vput(vp); 375 nfsm_srverr; 376 } 377 ivp->iov_base = mtod(mp, caddr_t); 378 if (left > mp->m_len) { 379 ivp->iov_len = mp->m_len; 380 left -= mp->m_len; 381 } else { 382 ivp->iov_len = mp->m_len = left; 383 left = 0; 384 } 385 ivp++; 386 } 387 error = VOP_READ(vp, uiop, &off, IO_NODELOCKED, cred); 388 if (error) { 389 m_freem(mp3); 390 vput(vp); 391 nfsm_reply(0); 392 } 393 if (error = VOP_GETATTR(vp, vap, cred)) 394 m_freem(mp3); 395 vput(vp); 396 nfsm_reply(NFSX_FATTR+NFSX_UNSIGNED); 397 nfsm_build(p, u_long *, NFSX_FATTR+NFSX_UNSIGNED); 398 *p++ = vtonfs_type(vap->va_type); 399 *p++ = vtonfs_mode(vap->va_type, vap->va_mode); 400 *p++ = txdr_unsigned(vap->va_nlink); 401 *p++ = txdr_unsigned(vap->va_uid); 402 *p++ = txdr_unsigned(vap->va_gid); 403 *p++ = txdr_unsigned(vap->va_size); 404 *p++ = txdr_unsigned(vap->va_blocksize); 405 *p++ = txdr_unsigned(vap->va_rdev); 406 *p++ = txdr_unsigned(vap->va_bytes); 407 *p++ = txdr_unsigned(vap->va_fsid); 408 *p++ = txdr_unsigned(vap->va_fileid); 409 txdr_time(&(vap->va_atime), p); 410 p += 2; 411 txdr_time(&(vap->va_mtime), p); 412 p += 2; 413 txdr_time(&(vap->va_ctime), p); 414 p += 2; 415 if (uiop->uio_resid > 0) { 416 len -= uiop->uio_resid; 417 if (len > 0) { 418 tlen = nfsm_rndup(len); 419 nfsm_adj(mp3, cnt-tlen, tlen-len); 420 } else { 421 m_freem(mp3); 422 mp3 = (struct mbuf *)0; 423 } 424 } 425 *p = txdr_unsigned(len); 426 mb->m_next = mp3; 427 nfsm_srvdone; 428 } 429 430 /* 431 * nfs write service 432 */ 433 nfsrv_write(mrep, md, dpos, cred, xid, mrq) 434 struct mbuf *mrep, *md, **mrq; 435 caddr_t dpos; 436 struct ucred *cred; 437 long xid; 438 { 439 register struct iovec *ivp; 440 register struct mbuf *mp; 441 struct iovec iv[MAX_IOVEC]; 442 struct vattr va; 443 register struct vattr *vap = &va; 444 nfsm_srvars; 445 struct vnode *vp; 446 nfsv2fh_t nfh; 447 fhandle_t *fhp; 448 struct uio io, *uiop = &io; 449 off_t off; 450 long siz, len, xfer; 451 452 fhp = &nfh.fh_generic; 453 nfsm_srvmtofh(fhp); 454 nfsm_disect(p, u_long *, 4*NFSX_UNSIGNED); 455 off = fxdr_unsigned(off_t, *++p); 456 p += 2; 457 len = fxdr_unsigned(long, *p); 458 if (len > NFS_MAXDATA || len <= 0) { 459 error = EBADRPC; 460 nfsm_reply(0); 461 } 462 if (dpos == (mtod(md, caddr_t)+md->m_len)) { 463 mp = md->m_next; 464 if (mp == NULL) { 465 error = EBADRPC; 466 nfsm_reply(0); 467 } 468 } else { 469 mp = md; 470 siz = dpos-mtod(mp, caddr_t); 471 mp->m_len -= siz; 472 NFSMADV(mp, siz); 473 } 474 if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred)) 475 nfsm_reply(0); 476 if (error = nfsrv_access(vp, VWRITE, cred)) { 477 vput(vp); 478 nfsm_reply(0); 479 } 480 uiop->uio_resid = 0; 481 uiop->uio_rw = UIO_WRITE; 482 uiop->uio_segflg = UIO_SYSSPACE; 483 /* 484 * Do up to MAX_IOVEC mbufs of write each iteration of the 485 * loop until done. 486 */ 487 while (len > 0 && uiop->uio_resid == 0) { 488 ivp = iv; 489 siz = 0; 490 uiop->uio_iov = ivp; 491 uiop->uio_iovcnt = 0; 492 uiop->uio_offset = off; 493 while (len > 0 && uiop->uio_iovcnt < MAX_IOVEC && mp != NULL) { 494 ivp->iov_base = mtod(mp, caddr_t); 495 if (len < mp->m_len) 496 ivp->iov_len = xfer = len; 497 else 498 ivp->iov_len = xfer = mp->m_len; 499 #ifdef notdef 500 /* Not Yet .. */ 501 if (M_HASCL(mp) && (((u_long)ivp->iov_base) & CLOFSET) == 0) 502 ivp->iov_op = NULL; /* what should it be ?? */ 503 else 504 ivp->iov_op = NULL; 505 #endif 506 uiop->uio_iovcnt++; 507 ivp++; 508 len -= xfer; 509 siz += xfer; 510 mp = mp->m_next; 511 } 512 if (len > 0 && mp == NULL) { 513 error = EBADRPC; 514 vput(vp); 515 nfsm_reply(0); 516 } 517 uiop->uio_resid = siz; 518 if (error = VOP_WRITE(vp, uiop, &off, IO_SYNC | IO_NODELOCKED, 519 cred)) { 520 vput(vp); 521 nfsm_reply(0); 522 } 523 } 524 error = VOP_GETATTR(vp, vap, cred); 525 vput(vp); 526 nfsm_reply(NFSX_FATTR); 527 nfsm_build(p, u_long *, NFSX_FATTR); 528 *p++ = vtonfs_type(vap->va_type); 529 *p++ = vtonfs_mode(vap->va_type, vap->va_mode); 530 *p++ = txdr_unsigned(vap->va_nlink); 531 *p++ = txdr_unsigned(vap->va_uid); 532 *p++ = txdr_unsigned(vap->va_gid); 533 *p++ = txdr_unsigned(vap->va_size); 534 *p++ = txdr_unsigned(vap->va_blocksize); 535 *p++ = txdr_unsigned(vap->va_rdev); 536 *p++ = txdr_unsigned(vap->va_bytes); 537 *p++ = txdr_unsigned(vap->va_fsid); 538 *p++ = txdr_unsigned(vap->va_fileid); 539 txdr_time(&(vap->va_atime), p); 540 p += 2; 541 txdr_time(&(vap->va_mtime), p); 542 p += 2; 543 txdr_time(&(vap->va_ctime), p); 544 nfsm_srvdone; 545 } 546 547 /* 548 * nfs create service 549 * now does a truncate to 0 length via. setattr if it already exists 550 */ 551 nfsrv_create(mrep, md, dpos, cred, xid, mrq) 552 struct mbuf *mrep, *md, **mrq; 553 caddr_t dpos; 554 struct ucred *cred; 555 long xid; 556 { 557 struct vattr va; 558 register struct vattr *vap = &va; 559 register struct nameidata *ndp = &u.u_nd; 560 nfsm_srvars; 561 struct vnode *vp; 562 nfsv2fh_t nfh; 563 fhandle_t *fhp; 564 long len; 565 566 ndp->ni_vp = ndp->ni_dvp = (struct vnode *)0; 567 fhp = &nfh.fh_generic; 568 nfsm_srvmtofh(fhp); 569 nfsm_srvstrsiz(len, NFS_MAXNAMLEN); 570 ndp->ni_cred = cred; 571 ndp->ni_nameiop = CREATE | LOCKPARENT | LOCKLEAF; 572 if (error = nfs_namei(ndp, fhp, len, &md, &dpos)) 573 nfsm_reply(0); 574 vattr_null(vap); 575 nfsm_disect(p, u_long *, NFSX_UNSIGNED); 576 /* 577 * Iff doesn't exist, create it 578 * otherwise just truncate to 0 length 579 * should I set the mode too ?? 580 */ 581 if (ndp->ni_vp == NULL) { 582 vap->va_type = VREG; 583 vap->va_mode = nfstov_mode(*p++); 584 if (error = VOP_CREATE(ndp, vap)) 585 nfsm_reply(0); 586 vp = ndp->ni_vp; 587 } else { 588 vp = ndp->ni_vp; 589 ndp->ni_vp = (struct vnode *)0; 590 VOP_ABORTOP(ndp); 591 vap->va_size = 0; 592 if (error = VOP_SETATTR(vp, vap, cred)) 593 nfsm_reply(0); 594 } 595 bzero((caddr_t)fhp, sizeof(nfh)); 596 fhp->fh_fsid = vp->v_mount->m_fsid; 597 if (error = VFS_VPTOFH(vp, &fhp->fh_fid)) { 598 vput(vp); 599 nfsm_reply(0); 600 } 601 error = VOP_GETATTR(vp, vap, cred); 602 vput(vp); 603 nfsm_reply(NFSX_FH+NFSX_FATTR); 604 nfsm_srvfhtom(fhp); 605 nfsm_build(p, u_long *, NFSX_FATTR); 606 *p++ = vtonfs_type(vap->va_type); 607 *p++ = vtonfs_mode(vap->va_type, vap->va_mode); 608 *p++ = txdr_unsigned(vap->va_nlink); 609 *p++ = txdr_unsigned(vap->va_uid); 610 *p++ = txdr_unsigned(vap->va_gid); 611 *p++ = txdr_unsigned(vap->va_size); 612 *p++ = txdr_unsigned(vap->va_blocksize); 613 *p++ = txdr_unsigned(vap->va_rdev); 614 *p++ = txdr_unsigned(vap->va_bytes); 615 *p++ = txdr_unsigned(vap->va_fsid); 616 *p++ = txdr_unsigned(vap->va_fileid); 617 txdr_time(&(vap->va_atime), p); 618 p += 2; 619 txdr_time(&(vap->va_mtime), p); 620 p += 2; 621 txdr_time(&(vap->va_ctime), p); 622 return (error); 623 nfsmout: 624 VOP_ABORTOP(ndp); 625 return (error); 626 } 627 628 /* 629 * nfs remove service 630 */ 631 nfsrv_remove(mrep, md, dpos, cred, xid, mrq) 632 struct mbuf *mrep, *md, **mrq; 633 caddr_t dpos; 634 struct ucred *cred; 635 long xid; 636 { 637 register struct nameidata *ndp = &u.u_nd; 638 nfsm_srvars; 639 struct vnode *vp; 640 nfsv2fh_t nfh; 641 fhandle_t *fhp; 642 long len; 643 644 fhp = &nfh.fh_generic; 645 nfsm_srvmtofh(fhp); 646 nfsm_srvstrsiz(len, NFS_MAXNAMLEN); 647 ndp->ni_cred = cred; 648 ndp->ni_nameiop = DELETE | LOCKPARENT | LOCKLEAF; 649 if (error = nfs_namei(ndp, fhp, len, &md, &dpos)) 650 nfsm_reply(0); 651 vp = ndp->ni_vp; 652 if (vp->v_type == VDIR && 653 (error = suser(cred, (short *)0))) 654 goto out; 655 /* 656 * Don't unlink a mounted file. 657 */ 658 if (vp->v_flag & VROOT) { 659 error = EBUSY; 660 goto out; 661 } 662 if (vp->v_flag & VTEXT) 663 xrele(vp); /* try once to free text */ 664 out: 665 if (error) 666 VOP_ABORTOP(ndp); 667 else 668 error = VOP_REMOVE(ndp); 669 nfsm_reply(0); 670 nfsm_srvdone; 671 } 672 673 /* 674 * nfs rename service 675 */ 676 nfsrv_rename(mrep, md, dpos, cred, xid, mrq) 677 struct mbuf *mrep, *md, **mrq; 678 caddr_t dpos; 679 struct ucred *cred; 680 long xid; 681 { 682 register struct nameidata *ndp; 683 nfsm_srvars; 684 struct nameidata tond; 685 struct vnode *fvp, *tvp, *tdvp; 686 nfsv2fh_t fnfh, tnfh; 687 fhandle_t *ffhp, *tfhp; 688 long len, len2; 689 int rootflg = 0; 690 691 ndp = &u.u_nd; 692 ndp->ni_vp = ndp->ni_dvp = (struct vnode *)0; 693 ffhp = &fnfh.fh_generic; 694 tfhp = &tnfh.fh_generic; 695 nfsm_srvmtofh(ffhp); 696 nfsm_srvstrsiz(len, NFS_MAXNAMLEN); 697 /* 698 * Remember if we are root so that we can reset cr_uid before 699 * the second nfs_namei() call 700 */ 701 if (cred->cr_uid == 0) 702 rootflg++; 703 ndp->ni_cred = cred; 704 ndp->ni_nameiop = DELETE | WANTPARENT; 705 if (error = nfs_namei(ndp, ffhp, len, &md, &dpos)) 706 nfsm_reply(0); 707 fvp = ndp->ni_vp; 708 nfsm_srvmtofh(tfhp); 709 nfsm_srvstrsiz(len2, NFS_MAXNAMLEN); 710 if (rootflg) 711 cred->cr_uid = 0; 712 nddup(ndp, &tond); 713 tond.ni_nameiop = RENAME | LOCKPARENT | LOCKLEAF | NOCACHE; 714 error = nfs_namei(&tond, tfhp, len2, &md, &dpos); 715 tdvp = tond.ni_dvp; 716 tvp = tond.ni_vp; 717 if (tvp != NULL) { 718 if (fvp->v_type == VDIR && tvp->v_type != VDIR) { 719 error = EISDIR; 720 goto out; 721 } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { 722 error = ENOTDIR; 723 goto out; 724 } 725 } 726 if (error) { 727 VOP_ABORTOP(ndp); 728 goto out1; 729 } 730 if (fvp->v_mount != tdvp->v_mount) { 731 error = EXDEV; 732 goto out; 733 } 734 if (fvp == tdvp || fvp == tvp) 735 error = EINVAL; 736 out: 737 if (error) { 738 VOP_ABORTOP(&tond); 739 VOP_ABORTOP(ndp); 740 } else { 741 VREF(tond.ni_cdir); 742 error = VOP_RENAME(ndp, &tond); 743 vrele(tond.ni_cdir); 744 } 745 out1: 746 ndrele(ndp); 747 nfsm_reply(0); 748 return (error); 749 nfsmout: 750 VOP_ABORTOP(ndp); 751 return (error); 752 } 753 754 /* 755 * nfs link service 756 */ 757 nfsrv_link(mrep, md, dpos, cred, xid, mrq) 758 struct mbuf *mrep, *md, **mrq; 759 caddr_t dpos; 760 struct ucred *cred; 761 long xid; 762 { 763 register struct nameidata *ndp = &u.u_nd; 764 nfsm_srvars; 765 struct vnode *vp, *xp; 766 nfsv2fh_t nfh, dnfh; 767 fhandle_t *fhp, *dfhp; 768 long len; 769 770 fhp = &nfh.fh_generic; 771 dfhp = &dnfh.fh_generic; 772 nfsm_srvmtofh(fhp); 773 nfsm_srvmtofh(dfhp); 774 nfsm_srvstrsiz(len, NFS_MAXNAMLEN); 775 if (error = nfsrv_fhtovp(fhp, FALSE, &vp, cred)) 776 nfsm_reply(0); 777 if (vp->v_type == VDIR && (error = suser(cred, NULL))) 778 goto out1; 779 ndp->ni_cred = cred; 780 ndp->ni_nameiop = CREATE | LOCKPARENT; 781 if (error = nfs_namei(ndp, dfhp, len, &md, &dpos)) 782 goto out1; 783 xp = ndp->ni_vp; 784 if (xp != NULL) { 785 error = EEXIST; 786 goto out; 787 } 788 xp = ndp->ni_dvp; 789 if (vp->v_mount != xp->v_mount) 790 error = EXDEV; 791 out: 792 if (error) 793 VOP_ABORTOP(ndp); 794 else 795 error = VOP_LINK(vp, ndp); 796 out1: 797 vrele(vp); 798 nfsm_reply(0); 799 nfsm_srvdone; 800 } 801 802 /* 803 * nfs symbolic link service 804 */ 805 nfsrv_symlink(mrep, md, dpos, cred, xid, mrq) 806 struct mbuf *mrep, *md, **mrq; 807 caddr_t dpos; 808 struct ucred *cred; 809 long xid; 810 { 811 struct vattr va; 812 register struct nameidata *ndp = &u.u_nd; 813 register struct vattr *vap = &va; 814 nfsm_srvars; 815 struct vnode *vp; 816 nfsv2fh_t nfh; 817 fhandle_t *fhp; 818 long len, tlen, len2; 819 820 ndp->ni_vp = ndp->ni_dvp = (struct vnode *)0; 821 fhp = &nfh.fh_generic; 822 nfsm_srvmtofh(fhp); 823 nfsm_srvstrsiz(len, NFS_MAXNAMLEN); 824 ndp->ni_cred = cred; 825 ndp->ni_nameiop = CREATE | LOCKPARENT; 826 if (error = nfs_namei(ndp, fhp, len, &md, &dpos)) 827 goto out1; 828 nfsm_srvstrsiz(len2, NFS_MAXPATHLEN); 829 tlen = nfsm_rndup(len2); 830 if (len2 == tlen) { 831 nfsm_disect(cp2, caddr_t, tlen+NFSX_UNSIGNED); 832 *(cp2+tlen) = '\0'; 833 } else { 834 nfsm_disect(cp2, caddr_t, tlen); 835 } 836 vp = ndp->ni_vp; 837 if (vp) { 838 error = EEXIST; 839 goto out; 840 } 841 vp = ndp->ni_dvp; 842 vattr_null(vap); 843 vap->va_mode = 0777; 844 out: 845 if (error) 846 VOP_ABORTOP(ndp); 847 else 848 error = VOP_SYMLINK(ndp, vap, cp2); 849 out1: 850 nfsm_reply(0); 851 return (error); 852 nfsmout: 853 VOP_ABORTOP(ndp); 854 return (error); 855 } 856 857 /* 858 * nfs mkdir service 859 */ 860 nfsrv_mkdir(mrep, md, dpos, cred, xid, mrq) 861 struct mbuf *mrep, *md, **mrq; 862 caddr_t dpos; 863 struct ucred *cred; 864 long xid; 865 { 866 struct vattr va; 867 register struct vattr *vap = &va; 868 register struct nameidata *ndp = &u.u_nd; 869 nfsm_srvars; 870 struct vnode *vp; 871 nfsv2fh_t nfh; 872 fhandle_t *fhp; 873 long len; 874 875 ndp->ni_vp = ndp->ni_dvp = (struct vnode *)0; 876 fhp = &nfh.fh_generic; 877 nfsm_srvmtofh(fhp); 878 nfsm_srvstrsiz(len, NFS_MAXNAMLEN); 879 ndp->ni_cred = cred; 880 ndp->ni_nameiop = CREATE | LOCKPARENT; 881 if (error = nfs_namei(ndp, fhp, len, &md, &dpos)) 882 nfsm_reply(0); 883 nfsm_disect(p, u_long *, NFSX_UNSIGNED); 884 vattr_null(vap); 885 vap->va_type = VDIR; 886 vap->va_mode = nfstov_mode(*p++); 887 vp = ndp->ni_vp; 888 if (vp != NULL) { 889 VOP_ABORTOP(ndp); 890 error = EEXIST; 891 nfsm_reply(0); 892 } 893 if (error = VOP_MKDIR(ndp, vap)) 894 nfsm_reply(0); 895 vp = ndp->ni_vp; 896 bzero((caddr_t)fhp, sizeof(nfh)); 897 fhp->fh_fsid = vp->v_mount->m_fsid; 898 if (error = VFS_VPTOFH(vp, &fhp->fh_fid)) { 899 vput(vp); 900 nfsm_reply(0); 901 } 902 error = VOP_GETATTR(vp, vap, cred); 903 vput(vp); 904 nfsm_reply(NFSX_FH+NFSX_FATTR); 905 nfsm_srvfhtom(fhp); 906 nfsm_build(p, u_long *, NFSX_FATTR); 907 *p++ = vtonfs_type(vap->va_type); 908 *p++ = vtonfs_mode(vap->va_type, vap->va_mode); 909 *p++ = txdr_unsigned(vap->va_nlink); 910 *p++ = txdr_unsigned(vap->va_uid); 911 *p++ = txdr_unsigned(vap->va_gid); 912 *p++ = txdr_unsigned(vap->va_size); 913 *p++ = txdr_unsigned(vap->va_blocksize); 914 *p++ = txdr_unsigned(vap->va_rdev); 915 *p++ = txdr_unsigned(vap->va_bytes); 916 *p++ = txdr_unsigned(vap->va_fsid); 917 *p++ = txdr_unsigned(vap->va_fileid); 918 txdr_time(&(vap->va_atime), p); 919 p += 2; 920 txdr_time(&(vap->va_mtime), p); 921 p += 2; 922 txdr_time(&(vap->va_ctime), p); 923 return (error); 924 nfsmout: 925 VOP_ABORTOP(ndp); 926 return (error); 927 } 928 929 /* 930 * nfs rmdir service 931 */ 932 nfsrv_rmdir(mrep, md, dpos, cred, xid, mrq) 933 struct mbuf *mrep, *md, **mrq; 934 caddr_t dpos; 935 struct ucred *cred; 936 long xid; 937 { 938 register struct nameidata *ndp = &u.u_nd; 939 nfsm_srvars; 940 struct vnode *vp; 941 nfsv2fh_t nfh; 942 fhandle_t *fhp; 943 long len; 944 945 fhp = &nfh.fh_generic; 946 nfsm_srvmtofh(fhp); 947 nfsm_srvstrsiz(len, NFS_MAXNAMLEN); 948 ndp->ni_cred = cred; 949 ndp->ni_nameiop = DELETE | LOCKPARENT | LOCKLEAF; 950 if (error = nfs_namei(ndp, fhp, len, &md, &dpos)) 951 nfsm_reply(0); 952 vp = ndp->ni_vp; 953 if (vp->v_type != VDIR) { 954 error = ENOTDIR; 955 goto out; 956 } 957 /* 958 * No rmdir "." please. 959 */ 960 if (ndp->ni_dvp == vp) { 961 error = EINVAL; 962 goto out; 963 } 964 /* 965 * Don't unlink a mounted file. 966 */ 967 if (vp->v_flag & VROOT) 968 error = EBUSY; 969 out: 970 if (error) 971 VOP_ABORTOP(ndp); 972 else 973 error = VOP_RMDIR(ndp); 974 nfsm_reply(0); 975 nfsm_srvdone; 976 } 977 978 /* 979 * nfs readdir service 980 * - mallocs what it thinks is enough to read 981 * count rounded up to a multiple of DIRBLKSIZ <= MAX_READDIR 982 * - calls VOP_READDIR() 983 * - loops arround building the reply 984 * if the output generated exceeds count break out of loop 985 * The nfsm_clget macro is used here so that the reply will be packed 986 * tightly in mbuf clusters. 987 * - it only knows that it has encountered eof when the VOP_READDIR() 988 * reads nothing 989 * - as such one readdir rpc will return eof false although you are there 990 * and then the next will return eof 991 * - it trims out records with d_ino == 0 992 * this doesn't matter for Unix clients, but they might confuse clients 993 * for other os'. 994 * NB: It is tempting to set eof to true if the VOP_READDIR() reads less 995 * than requested, but this may not apply to all filesystems. For 996 * example, client NFS does not { although it is never remote mounted 997 * anyhow } 998 * PS: The NFS protocol spec. does not clarify what the "count" byte 999 * argument is a count of.. just name strings and file id's or the 1000 * entire reply rpc or ... 1001 * I tried just file name and id sizes and it confused the Sun client, 1002 * so I am using the full rpc size now. The "paranoia.." comment refers 1003 * to including the status longwords that are not a part of the dir. 1004 * "entry" structures, but are in the rpc. 1005 */ 1006 nfsrv_readdir(mrep, md, dpos, cred, xid, mrq) 1007 struct mbuf **mrq; 1008 struct mbuf *mrep, *md; 1009 caddr_t dpos; 1010 struct ucred *cred; 1011 u_long xid; 1012 { 1013 register char *bp, *be; 1014 register struct mbuf *mp; 1015 register struct direct *dp; 1016 nfsm_srvars; 1017 char *cpos, *cend; 1018 int len, nlen, rem, xfer, tsiz, i; 1019 struct vnode *vp; 1020 struct mbuf *mp2, *mp3; 1021 nfsv2fh_t nfh; 1022 fhandle_t *fhp; 1023 struct uio io; 1024 struct iovec iv; 1025 int siz, cnt, fullsiz; 1026 u_long on; 1027 char *rbuf; 1028 off_t off, toff; 1029 1030 fhp = &nfh.fh_generic; 1031 nfsm_srvmtofh(fhp); 1032 nfsm_disect(p, u_long *, 2*NFSX_UNSIGNED); 1033 toff = fxdr_unsigned(off_t, *p++); 1034 off = (toff & ~(DIRBLKSIZ-1)); 1035 on = (toff & (DIRBLKSIZ-1)); 1036 cnt = fxdr_unsigned(int, *p); 1037 siz = ((cnt+DIRBLKSIZ) & ~(DIRBLKSIZ-1)); 1038 if (cnt > MAX_READDIR) 1039 siz = MAX_READDIR; 1040 fullsiz = siz; 1041 if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred)) 1042 nfsm_reply(0); 1043 if (error = nfsrv_access(vp, VEXEC, cred)) { 1044 vput(vp); 1045 nfsm_reply(0); 1046 } 1047 VOP_UNLOCK(vp); 1048 MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK); 1049 again: 1050 iv.iov_base = rbuf; 1051 iv.iov_len = fullsiz; 1052 io.uio_iov = &iv; 1053 io.uio_iovcnt = 1; 1054 io.uio_offset = off; 1055 io.uio_resid = fullsiz; 1056 io.uio_segflg = UIO_SYSSPACE; 1057 io.uio_rw = UIO_READ; 1058 error = VOP_READDIR(vp, &io, &off, cred); 1059 if (error) { 1060 vrele(vp); 1061 free((caddr_t)rbuf, M_TEMP); 1062 nfsm_reply(0); 1063 } 1064 if (io.uio_resid) { 1065 siz -= io.uio_resid; 1066 1067 /* 1068 * If nothing read, return eof 1069 * rpc reply 1070 */ 1071 if (siz == 0) { 1072 vrele(vp); 1073 nfsm_reply(2*NFSX_UNSIGNED); 1074 nfsm_build(p, u_long *, 2*NFSX_UNSIGNED); 1075 *p++ = nfs_false; 1076 *p = nfs_true; 1077 FREE((caddr_t)rbuf, M_TEMP); 1078 return (0); 1079 } 1080 } 1081 cpos = rbuf+on; 1082 cend = rbuf+siz; 1083 dp = (struct direct *)cpos; 1084 /* 1085 * Check for degenerate cases of nothing useful read. 1086 * If so go try again 1087 */ 1088 if (cpos >= cend || (dp->d_ino == 0 && (cpos+dp->d_reclen) >= cend)) { 1089 toff = off; 1090 siz = fullsiz; 1091 on = 0; 1092 goto again; 1093 } 1094 vrele(vp); 1095 len = 3*NFSX_UNSIGNED; /* paranoia, probably can be 0 */ 1096 bp = be = (caddr_t)0; 1097 mp3 = (struct mbuf *)0; 1098 nfsm_reply(siz); 1099 1100 /* Loop through the records and build reply */ 1101 while (cpos < cend) { 1102 if (dp->d_ino != 0) { 1103 nlen = dp->d_namlen; 1104 rem = nfsm_rndup(nlen)-nlen; 1105 1106 /* 1107 * As noted above, the NFS spec. is not clear about what 1108 * should be included in "count" as totalled up here in 1109 * "len". 1110 */ 1111 len += (4*NFSX_UNSIGNED+nlen+rem); 1112 if (len > cnt) 1113 break; 1114 1115 /* Build the directory record xdr from the direct entry */ 1116 nfsm_clget; 1117 *p = nfs_true; 1118 bp += NFSX_UNSIGNED; 1119 nfsm_clget; 1120 *p = txdr_unsigned(dp->d_ino); 1121 bp += NFSX_UNSIGNED; 1122 nfsm_clget; 1123 *p = txdr_unsigned(nlen); 1124 bp += NFSX_UNSIGNED; 1125 1126 /* And loop arround copying the name */ 1127 xfer = nlen; 1128 cp = dp->d_name; 1129 while (xfer > 0) { 1130 nfsm_clget; 1131 if ((bp+xfer) > be) 1132 tsiz = be-bp; 1133 else 1134 tsiz = xfer; 1135 bcopy(cp, bp, tsiz); 1136 bp += tsiz; 1137 xfer -= tsiz; 1138 if (xfer > 0) 1139 cp += tsiz; 1140 } 1141 /* And null pad to a long boundary */ 1142 for (i = 0; i < rem; i++) 1143 *bp++ = '\0'; 1144 nfsm_clget; 1145 1146 /* Finish off the record */ 1147 toff += dp->d_reclen; 1148 *p = txdr_unsigned(toff); 1149 bp += NFSX_UNSIGNED; 1150 } else 1151 toff += dp->d_reclen; 1152 cpos += dp->d_reclen; 1153 dp = (struct direct *)cpos; 1154 } 1155 nfsm_clget; 1156 *p = nfs_false; 1157 bp += NFSX_UNSIGNED; 1158 nfsm_clget; 1159 *p = nfs_false; 1160 bp += NFSX_UNSIGNED; 1161 if (bp < be) 1162 mp->m_len = bp-mtod(mp, caddr_t); 1163 mb->m_next = mp3; 1164 FREE(rbuf, M_TEMP); 1165 nfsm_srvdone; 1166 } 1167 1168 /* 1169 * nfs statfs service 1170 */ 1171 nfsrv_statfs(mrep, md, dpos, cred, xid, mrq) 1172 struct mbuf **mrq; 1173 struct mbuf *mrep, *md; 1174 caddr_t dpos; 1175 struct ucred *cred; 1176 u_long xid; 1177 { 1178 register struct statfs *sf; 1179 nfsm_srvars; 1180 struct vnode *vp; 1181 nfsv2fh_t nfh; 1182 fhandle_t *fhp; 1183 struct statfs statfs; 1184 1185 fhp = &nfh.fh_generic; 1186 nfsm_srvmtofh(fhp); 1187 if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred)) 1188 nfsm_reply(0); 1189 sf = &statfs; 1190 error = VFS_STATFS(vp->v_mount, sf); 1191 vput(vp); 1192 nfsm_reply(NFSX_STATFS); 1193 nfsm_build(p, u_long *, NFSX_STATFS); 1194 *p++ = txdr_unsigned(NFS_MAXDATA); 1195 *p++ = txdr_unsigned(sf->f_fsize); 1196 *p++ = txdr_unsigned(sf->f_blocks); 1197 *p++ = txdr_unsigned(sf->f_bfree); 1198 *p = txdr_unsigned(sf->f_bavail); 1199 nfsm_srvdone; 1200 } 1201 1202 /* 1203 * Null operation, used by clients to ping server 1204 */ 1205 nfsrv_null(mrep, md, dpos, cred, xid, mrq) 1206 struct mbuf **mrq; 1207 struct mbuf *mrep, *md; 1208 caddr_t dpos; 1209 struct ucred *cred; 1210 u_long xid; 1211 { 1212 nfsm_srvars; 1213 1214 error = VNOVAL; 1215 nfsm_reply(0); 1216 nfsm_srvdone; 1217 } 1218 1219 /* 1220 * No operation, used for obsolete procedures 1221 */ 1222 nfsrv_noop(mrep, md, dpos, cred, xid, mrq) 1223 struct mbuf **mrq; 1224 struct mbuf *mrep, *md; 1225 caddr_t dpos; 1226 struct ucred *cred; 1227 u_long xid; 1228 { 1229 nfsm_srvars; 1230 1231 error = EPROCUNAVAIL; 1232 nfsm_reply(0); 1233 nfsm_srvdone; 1234 } 1235 1236 /* 1237 * Perform access checking for vnodes obtained from file handles that would 1238 * refer to files already opened by a Unix client. You cannot just use 1239 * vn_writechk() and VOP_ACCESS() for two reasons. 1240 * 1 - You must check for M_EXRDONLY as well as M_RDONLY for the write case 1241 * 2 - The owner is to be given access irrespective of mode bits so that 1242 * processes that chmod after opening a file don't break. I don't like 1243 * this because it opens a security hole, but since the nfs server opens 1244 * a security hole the size of a barn door anyhow, what the heck. 1245 */ 1246 nfsrv_access(vp, flags, cred) 1247 register struct vnode *vp; 1248 int flags; 1249 register struct ucred *cred; 1250 { 1251 struct vattr vattr; 1252 int error; 1253 if (flags & VWRITE) { 1254 /* Just vn_writechk() changed to check M_EXRDONLY */ 1255 /* 1256 * Disallow write attempts on read-only file systems; 1257 * unless the file is a socket or a block or character 1258 * device resident on the file system. 1259 */ 1260 if ((vp->v_mount->m_flag & (M_RDONLY | M_EXRDONLY)) && 1261 vp->v_type != VCHR && 1262 vp->v_type != VBLK && 1263 vp->v_type != VSOCK) 1264 return (EROFS); 1265 /* 1266 * If there's shared text associated with 1267 * the inode, try to free it up once. If 1268 * we fail, we can't allow writing. 1269 */ 1270 if (vp->v_flag & VTEXT) 1271 xrele(vp); 1272 if (vp->v_flag & VTEXT) 1273 return (ETXTBSY); 1274 if (error = VOP_GETATTR(vp, &vattr, cred)) 1275 return (error); 1276 if (cred->cr_uid == vattr.va_uid) 1277 return (0); 1278 } else { 1279 if (error = VOP_GETATTR(vp, &vattr, cred)) 1280 return (error); 1281 if (cred->cr_uid == vattr.va_uid) 1282 return (0); 1283 } 1284 return (VOP_ACCESS(vp, flags, cred)); 1285 } 1286