1 /* 2 * Copyright (c) 1993 Paul Kranenburg 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by Paul Kranenburg. 16 * 4. The name of the author may not be used to endorse or promote products 17 * derived from this software withough specific prior written permission 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * 30 * $Id: procfs_vnops.c,v 1.7 1993/09/16 22:08:30 cgd Exp $ 31 */ 32 33 /* 34 * PROCFS vnode interface routines 35 */ 36 37 #include "param.h" 38 #include "systm.h" 39 #include "time.h" 40 #include "kernel.h" 41 #include "ioctl.h" 42 #include "file.h" 43 #include "proc.h" 44 #include "buf.h" 45 #include "vnode.h" 46 #include "namei.h" 47 #include "resourcevar.h" 48 #include "vm/vm.h" 49 #include "kinfo.h" 50 #include "kinfo_proc.h" 51 52 #include "procfs.h" 53 #include "pfsnode.h" 54 55 #include "machine/vmparam.h" 56 57 /* 58 * procfs vnode operations. 59 */ 60 struct vnodeops pfs_vnodeops = { 61 pfs_lookup, /* lookup */ 62 pfs_create, /* create */ 63 pfs_mknod, /* mknod */ 64 pfs_open, /* open */ 65 pfs_close, /* close */ 66 pfs_access, /* access */ 67 pfs_getattr, /* getattr */ 68 pfs_setattr, /* setattr */ 69 pfs_read, /* read */ 70 pfs_write, /* write */ 71 pfs_ioctl, /* ioctl */ 72 pfs_select, /* select */ 73 pfs_mmap, /* mmap */ 74 pfs_fsync, /* fsync */ 75 pfs_seek, /* seek */ 76 pfs_remove, /* remove */ 77 pfs_link, /* link */ 78 pfs_rename, /* rename */ 79 pfs_mkdir, /* mkdir */ 80 pfs_rmdir, /* rmdir */ 81 pfs_symlink, /* symlink */ 82 pfs_readdir, /* readdir */ 83 pfs_readlink, /* readlink */ 84 pfs_abortop, /* abortop */ 85 pfs_inactive, /* inactive */ 86 pfs_reclaim, /* reclaim */ 87 pfs_lock, /* lock */ 88 pfs_unlock, /* unlock */ 89 pfs_bmap, /* bmap */ 90 pfs_strategy, /* strategy */ 91 pfs_print, /* print */ 92 pfs_islocked, /* islocked */ 93 pfs_advlock, /* advlock */ 94 }; 95 96 /* 97 * Vnode Operations. 98 * 99 */ 100 /* ARGSUSED */ 101 int 102 pfs_open(vp, mode, cred, p) 103 register struct vnode *vp; 104 int mode; 105 struct ucred *cred; 106 struct proc *p; 107 { 108 struct pfsnode *pfsp = VTOPFS(vp); 109 110 #ifdef DEBUG 111 if (pfs_debug) 112 printf("pfs_open: vp 0x%x, proc %d\n", vp, p->p_pid); 113 #endif 114 115 if ((pfsp->pfs_pid?pfind(pfsp->pfs_pid):&proc0) == NULL) 116 return ESRCH; 117 118 if ( (pfsp->pfs_flags & FWRITE) && (mode & O_EXCL) || 119 (pfsp->pfs_flags & O_EXCL) && (mode & FWRITE) ) 120 return EBUSY; 121 122 123 if (mode & FWRITE) 124 pfsp->pfs_flags = (mode & (FWRITE|O_EXCL)); 125 return 0; 126 } 127 128 /* 129 * /proc filesystem close routine 130 */ 131 /* ARGSUSED */ 132 int 133 pfs_close(vp, flag, cred, p) 134 register struct vnode *vp; 135 int flag; 136 struct ucred *cred; 137 struct proc *p; 138 { 139 struct pfsnode *pfsp = VTOPFS(vp); 140 141 #ifdef DEBUG 142 if (pfs_debug) 143 printf("pfs_close: vp 0x%x proc %d\n", vp, p->p_pid); 144 #endif 145 if ((flag & FWRITE) && (pfsp->pfs_flags & O_EXCL)) 146 pfsp->pfs_flags &= ~(FWRITE|O_EXCL); 147 148 return (0); 149 } 150 151 /* 152 * Ioctl operation. 153 */ 154 /* ARGSUSED */ 155 int 156 pfs_ioctl(vp, com, data, fflag, cred, p) 157 struct vnode *vp; 158 int com; 159 caddr_t data; 160 int fflag; 161 struct ucred *cred; 162 struct proc *p; 163 { 164 int error = 0; 165 struct proc *procp; 166 struct pfsnode *pfsp = VTOPFS(vp); 167 168 procp = pfsp->pfs_pid?pfind(pfsp->pfs_pid):&proc0; 169 if (!procp) 170 return ESRCH; 171 172 switch (com) { 173 174 case PIOCGPINFO: { 175 int copysize = sizeof(struct kinfo_proc), needed; 176 kinfo_doproc(KINFO_PROC_PID, data, ©size, 177 pfsp->pfs_pid, &needed); 178 break; 179 } 180 181 #ifdef notyet /* Changes to proc.h needed */ 182 case PIOCGSIGSET: 183 procp->p_psigset = *(sigset_t *)data; 184 break; 185 186 case PIOCSSIGSET: 187 *(sigset_t *)data = procp->p_psigset; 188 break; 189 190 case PIOCGFLTSET: 191 procp->p_pfltset = *(sigflt_t *)data; 192 break; 193 194 case PIOCSFLTSET: 195 *(fltset_t *)data = procp->p_pfltset; 196 break; 197 #endif 198 199 case PIOCGMAPFD: 200 error = pfs_vmfd(procp, pfsp, (struct vmfd *)data, p); 201 break; 202 203 case PIOCGNMAP: 204 *(int *)data = pfs_vm_nentries(procp, pfsp); 205 break; 206 207 case PIOCGMAP: 208 error = pfs_vmmap(procp, pfsp, *(struct procmap *)data); 209 break; 210 211 default: 212 error = EIO; 213 break; 214 } 215 return error; 216 } 217 218 /* 219 * Pass I/O requests to the memory filesystem process. 220 */ 221 int 222 pfs_strategy(bp) 223 register struct buf *bp; 224 { 225 struct vnode *vp; 226 struct proc *p = curproc; /* XXX */ 227 228 return (0); 229 } 230 231 /* 232 * This is a noop, simply returning what one has been given. 233 */ 234 int 235 pfs_bmap(vp, bn, vpp, bnp) 236 struct vnode *vp; 237 daddr_t bn; 238 struct vnode **vpp; 239 daddr_t *bnp; 240 { 241 242 if (vpp != NULL) 243 *vpp = vp; 244 if (bnp != NULL) 245 *bnp = bn; 246 return (0); 247 } 248 249 /* 250 * /proc filesystem inactive routine 251 */ 252 /* ARGSUSED */ 253 int 254 pfs_inactive(vp, p) 255 struct vnode *vp; 256 struct proc *p; 257 { 258 struct pfsnode *pfsp = VTOPFS(vp); 259 260 if ((pfsp->pfs_pid?pfind(pfsp->pfs_pid):&proc0) == NULL 261 && vp->v_usecount == 0) 262 vgone(vp); 263 264 return 0; 265 } 266 267 /* 268 * /proc filesystem reclaim routine 269 */ 270 /* ARGSUSED */ 271 int 272 pfs_reclaim(vp) 273 struct vnode *vp; 274 { 275 struct pfsnode **pp, *pfsp = VTOPFS(vp); 276 277 for (pp = &pfshead; *pp; pp = &(*pp)->pfs_next) { 278 if (*pp == pfsp) { 279 *pp = pfsp->pfs_next; 280 break; 281 } 282 } 283 return 0; 284 } 285 286 /* 287 * Print out the contents of an pfsnode. 288 */ 289 void 290 pfs_print(vp) 291 struct vnode *vp; 292 { 293 struct pfsnode *pfsp = VTOPFS(vp); 294 295 printf("tag VT_PROCFS, pid %d, uid %d, gid %d, mode %x, flags %x\n", 296 pfsp->pfs_pid, 297 pfsp->pfs_uid, pfsp->pfs_gid, 298 pfsp->pfs_mode, pfsp->pfs_flags); 299 300 return; 301 } 302 303 /* 304 * /proc bad operation 305 */ 306 int 307 pfs_badop() 308 { 309 printf("pfs_badop called\n"); 310 return EIO; 311 } 312 313 #if 0 /* Moved to pfs_subr.c */ 314 /* 315 * Vnode op for reading/writing. 316 */ 317 /* ARGSUSED */ 318 int 319 pfs_doio(vp, uio, ioflag, cred) 320 struct vnode *vp; 321 register struct uio *uio; 322 int ioflag; 323 struct ucred *cred; 324 { 325 struct pfsnode *pfsp = VTOPFS(vp); 326 struct proc *procp; 327 int error = 0; 328 long n, off; 329 caddr_t kva; 330 331 #ifdef DEBUG 332 if (pfs_debug) 333 printf("pfs_doio(%s): vp 0x%x, proc %x\n", 334 uio->uio_rw==UIO_READ?"R":"W", vp, uio->uio_procp); 335 #endif 336 337 #ifdef DIAGNOSTIC 338 if (vp->v_type != VPROC) 339 panic("pfs_doio vtype"); 340 #endif 341 procp = pfsp->pfs_pid?pfind(pfsp->pfs_pid):&proc0; 342 if (!procp) 343 return ESRCH; 344 345 if (uio->uio_resid == 0) 346 return (0); 347 if (uio->uio_offset < 0) 348 return (EINVAL); 349 350 do { /* One page at a time */ 351 off = uio->uio_offset - trunc_page(uio->uio_offset); 352 n = MIN(PAGE_SIZE-off, uio->uio_resid); 353 354 /* Map page into kernel space */ 355 error = pfs_map(procp, &kva, uio->uio_rw, uio->uio_offset); 356 if (error) 357 return error; 358 359 error = uiomove(kva + off, (int)n, uio); 360 pfs_unmap(procp, kva); 361 362 } while (error == 0 && uio->uio_resid > 0); 363 364 return (error); 365 } 366 #endif 367 368 /* 369 * Make up some attributes for a process file 370 */ 371 int 372 pfs_getattr (vp, vap, cred, p) 373 struct vnode *vp; 374 struct vattr *vap; 375 struct ucred *cred; 376 struct proc *p; 377 { 378 struct pfsnode *pfsp = VTOPFS(vp); 379 struct proc *procp; 380 381 VATTR_NULL(vap); 382 vap->va_type = vp->v_type; 383 vap->va_mode = pfsp->pfs_mode; 384 vap->va_flags = pfsp->pfs_vflags; 385 386 if (vp->v_flag & VROOT) { 387 vap->va_nlink = 2; 388 vap->va_size = 389 roundup((2+nprocs)*sizeof(struct pfsdent), DIRBLKSIZ); 390 vap->va_size_rsv = 0; 391 vap->va_uid = pfsp->pfs_uid; 392 vap->va_gid = pfsp->pfs_gid; 393 vap->va_atime = vap->va_mtime = vap->va_ctime = time; /*XXX*/ 394 return 0; 395 } 396 397 procp = pfsp->pfs_pid?pfind(pfsp->pfs_pid):&proc0; 398 if (!procp) 399 return ESRCH; 400 401 vap->va_nlink = 1; 402 vap->va_size = ctob( procp->p_vmspace->vm_tsize + 403 procp->p_vmspace->vm_dsize + 404 procp->p_vmspace->vm_ssize); 405 vap->va_size_rsv = 0; 406 vap->va_blocksize = page_size; 407 vap->va_uid = procp->p_ucred->cr_uid; 408 vap->va_gid = procp->p_ucred->cr_gid; 409 if (vap->va_uid != procp->p_cred->p_ruid) 410 vap->va_mode |= VSUID; 411 if (vap->va_gid != procp->p_cred->p_rgid) 412 vap->va_mode |= VSGID; 413 if (procp->p_flag & SLOAD) { 414 vap->va_atime = vap->va_mtime = vap->va_ctime = 415 procp->p_stats->p_start; 416 } 417 418 return 0; 419 } 420 421 /* 422 * Set some attributes for a process file 423 */ 424 int 425 pfs_setattr (vp, vap, cred, p) 426 struct vnode *vp; 427 struct vattr *vap; 428 struct ucred *cred; 429 struct proc *p; 430 { 431 struct pfsnode *pfsp = VTOPFS(vp); 432 struct proc *procp; 433 int error = 0; 434 435 procp = pfsp->pfs_pid?pfind(pfsp->pfs_pid):&proc0; 436 if (!procp) 437 return ESRCH; 438 439 /* 440 * Check for unsetable attributes. 441 */ 442 if ((vap->va_type != VNON) || (vap->va_nlink != (short)VNOVAL) || 443 (vap->va_fsid != (long)VNOVAL) || 444 (vap->va_fileid != (long)VNOVAL) || 445 (vap->va_blocksize != (long)VNOVAL) || 446 (vap->va_rdev != (dev_t)VNOVAL) || 447 ((int)vap->va_bytes != (u_long)VNOVAL) || 448 ((int)vap->va_bytes_rsv != (u_long)VNOVAL) || 449 ((int)vap->va_size != (u_long)VNOVAL) || 450 ((int)vap->va_size_rsv != (u_long)VNOVAL) || 451 (vap->va_gen != (long)VNOVAL) || 452 ((int)vap->va_atime.tv_sec != (u_long)VNOVAL) || 453 ((int)vap->va_mtime.tv_sec != (u_long)VNOVAL) || 454 ((int)vap->va_ctime.tv_sec != (u_long)VNOVAL) || 455 (( (vap->va_uid != (uid_t)VNOVAL) || 456 (vap->va_gid != (gid_t)VNOVAL)) && !(vp->v_flag & VROOT)) ) { 457 return (EINVAL); 458 } 459 460 /* set mode bits, only rwx bits are modified */ 461 if (vap->va_mode != (u_short)VNOVAL) { 462 if (cred->cr_uid != pfsp->pfs_uid && 463 (error = suser(cred, &p->p_acflag))) 464 return (error); 465 pfsp->pfs_mode = vap->va_mode & 0777; 466 } 467 468 /* For now, only allow to change ownership of "/proc" itself */ 469 if ((vp->v_flag & VROOT) && vap->va_uid != (uid_t)VNOVAL) { 470 if ((error = suser(cred, &p->p_acflag))) 471 return (error); 472 pfsp->pfs_uid = vap->va_uid; 473 } 474 475 if ((vp->v_flag & VROOT) && vap->va_gid != (gid_t)VNOVAL) { 476 if ((cred->cr_uid != pfsp->pfs_uid || 477 !groupmember(vap->va_gid, cred)) && 478 (error = suser(cred, &p->p_acflag))) 479 return error; 480 481 pfsp->pfs_gid = vap->va_gid; 482 } 483 484 /* chflags() */ 485 if (vap->va_flags != (u_long)VNOVAL) { 486 if (cred->cr_uid != pfsp->pfs_uid && 487 (error = suser(cred, &p->p_acflag))) 488 return (error); 489 if (cred->cr_uid == 0) { 490 pfsp->pfs_vflags = vap->va_flags; 491 } else { 492 pfsp->pfs_vflags &= 0xffff0000; 493 pfsp->pfs_vflags |= (vap->va_flags & 0xffff); 494 } 495 } 496 return 0; 497 } 498 499 int 500 pfs_access (vp, mode, cred, p) 501 struct vnode *vp; 502 int mode; 503 struct ucred *cred; 504 struct proc *p; 505 { 506 register struct vattr *vap; 507 register gid_t *gp; 508 struct vattr vattr; 509 register int i; 510 int error; 511 512 /* 513 * If you're the super-user, 514 * you always get access. 515 */ 516 if (cred->cr_uid == (uid_t)0) 517 return (0); 518 vap = &vattr; 519 if (error = pfs_getattr(vp, vap, cred, p)) 520 return (error); 521 /* 522 * Access check is based on only one of owner, group, public. 523 * If not owner, then check group. If not a member of the 524 * group, then check public access. 525 */ 526 if (cred->cr_uid != vap->va_uid) { 527 mode >>= 3; 528 gp = cred->cr_groups; 529 for (i = 0; i < cred->cr_ngroups; i++, gp++) 530 if (vap->va_gid == *gp) 531 goto found; 532 mode >>= 3; 533 found: 534 ; 535 } 536 if ((vap->va_mode & mode) != 0) 537 return (0); 538 return (EACCES); 539 } 540 541 /* 542 * /proc lookup 543 */ 544 int 545 pfs_lookup(vp, ndp, p) 546 register struct vnode *vp; 547 register struct nameidata *ndp; 548 struct proc *p; 549 { 550 int lockparent, wantparent, flag, error = 0; 551 pid_t pid; 552 struct vnode *nvp; 553 struct pfsnode *pfsp; 554 struct proc *procp; 555 556 #ifdef DEBUG 557 if (pfs_debug) 558 printf("pfs_lookup: vp 0x%x name %s proc %d\n", 559 vp, ndp->ni_ptr, p->p_pid); 560 #endif 561 562 ndp->ni_dvp = vp; 563 ndp->ni_vp = NULL; 564 if (vp->v_type != VDIR) 565 return (ENOTDIR); 566 567 lockparent = ndp->ni_nameiop & LOCKPARENT; 568 flag = ndp->ni_nameiop & OPMASK; 569 wantparent = ndp->ni_nameiop & (LOCKPARENT|WANTPARENT); 570 if (flag != LOOKUP) 571 return EACCES; 572 if (ndp->ni_isdotdot) { 573 /* Should not happen */ 574 printf("pfs_lookup: vp 0x%x: dotdot\n", vp); 575 return EIO; 576 } 577 if (ndp->ni_namelen == 1 && *ndp->ni_ptr == '.') { 578 VREF(vp); 579 ndp->ni_vp = vp; 580 return 0; 581 } 582 583 pid = (pid_t)atoi(ndp->ni_ptr, ndp->ni_namelen); 584 if (pid == (pid_t)-1) 585 return ENOENT; 586 587 if ((procp = pid?pfind(pid):&proc0) == NULL) 588 return ENOENT; 589 590 /* Search pfs node list first */ 591 for (pfsp = pfshead; pfsp != NULL; pfsp = pfsp->pfs_next) { 592 if (pfsp->pfs_pid == pid) 593 break; 594 } 595 596 if (pfsp == NULL) { 597 struct pfsnode **pp; 598 error = getnewvnode(VT_PROCFS, vp->v_mount, &pfs_vnodeops, &nvp); 599 if (error) 600 return error; 601 602 nvp->v_type = VPROC; 603 pfsp = VTOPFS(nvp); 604 pfsp->pfs_next = NULL; 605 pfsp->pfs_pid = pid; 606 pfsp->pfs_vnode = nvp; 607 pfsp->pfs_flags = 0; 608 pfsp->pfs_vflags = 0; 609 pfsp->pfs_uid = procp->p_ucred->cr_uid; 610 pfsp->pfs_gid = procp->p_ucred->cr_gid; 611 pfsp->pfs_mode = 0700; /* Initial access bits */ 612 613 /* Append to pfs node list */ 614 for (pp = &pfshead; *pp; pp = &(*pp)->pfs_next); 615 *pp = pfsp; 616 617 } 618 ndp->ni_vp = pfsp->pfs_vnode; 619 620 return (error); 621 } 622 623 int 624 pfs_readdir(vp, uio, cred, eofflagp, cookies, ncookies) 625 struct vnode *vp; 626 register struct uio *uio; 627 struct ucred *cred; 628 int *eofflagp; 629 u_int *cookies; 630 int ncookies; 631 { 632 int error = 0; 633 int count, lost, pcnt, skipcnt, doingzomb = 0; 634 struct proc *p; 635 struct pfsdent dent; 636 637 #ifdef DEBUG 638 if (pfs_debug) 639 printf("pfs_readdir: vp 0x%x proc %d\n", 640 vp, uio->uio_procp->p_pid); 641 #endif 642 count = uio->uio_resid; 643 count &= ~(DIRBLKSIZ - 1); 644 lost = uio->uio_resid - count; 645 if (count < DIRBLKSIZ || (uio->uio_offset & (DIRBLKSIZ -1))) 646 return (EINVAL); 647 uio->uio_resid = count; 648 uio->uio_iov->iov_len = count; 649 *eofflagp = 1; 650 skipcnt = uio->uio_offset / sizeof(struct pfsdent); 651 652 count = 0; 653 if (skipcnt == 0) { 654 /* Fake "." and ".." entries? */ 655 #if 1 656 dent.d_fileno = 2; /* XXX - Filesystem root */ 657 dent.d_reclen = sizeof(struct pfsdent); 658 659 dent.d_namlen = 1; 660 dent.d_nam[0] = '.'; 661 dent.d_nam[1] = '\0'; 662 error = uiomove((char *)&dent, sizeof(struct pfsdent) , uio); 663 if (error) 664 return error; 665 if (cookies) { 666 *cookies++ = sizeof(struct pfsdent); 667 ncookies--; 668 } 669 670 dent.d_fileno = 2; 671 dent.d_namlen = 2; 672 dent.d_nam[1] = '.'; 673 dent.d_nam[2] = '\0'; 674 error = uiomove((char *)&dent, sizeof(struct pfsdent) , uio); 675 if (error) 676 return error; 677 if (cookies) { 678 *cookies++ = 2 * sizeof(struct pfsdent); 679 ncookies--; 680 } 681 #endif 682 count += 2*dent.d_reclen; 683 } 684 685 p = (struct proc *)allproc; 686 for (pcnt = 0; p && uio->uio_resid && (!cookies || ncookies > 0); pcnt++) { 687 if (pcnt < skipcnt) { 688 p = p->p_nxt; 689 if (p == NULL && doingzomb == 0) { 690 doingzomb = 1; 691 p = zombproc; 692 } 693 continue; 694 } 695 *eofflagp = 0; 696 697 /* "inode" is process slot (actually position on list) */ 698 dent.d_fileno = (unsigned long)(pcnt+1); 699 dent.d_namlen = itos((unsigned int)p->p_pid, dent.d_nam); 700 dent.d_nam[dent.d_namlen] = '\0'; 701 702 p = p->p_nxt; 703 if (p == NULL && doingzomb == 0) { 704 doingzomb = 1; 705 p = zombproc; 706 } 707 if (p == NULL) { 708 /* Extend 'reclen' to end of block */; 709 dent.d_reclen = DIRBLKSIZ - (count & (DIRBLKSIZ - 1)); 710 } else 711 dent.d_reclen = sizeof(struct pfsdent); 712 count += dent.d_reclen; 713 error = uiomove((char *)&dent, dent.d_reclen, uio); 714 if (error) 715 break; 716 if (cookies) { 717 *cookies++ = count; 718 ncookies--; 719 } 720 } 721 if (count == 0) 722 *eofflagp = 1; 723 724 uio->uio_resid += lost; 725 return error; 726 } 727 728 /* 729 * convert n to decimal representation in character array b 730 * return number of decimal digits produced. 731 */ 732 int 733 itos(n, b) 734 unsigned int n; 735 char *b; 736 { 737 #define BASE 10 738 int m = (n<BASE)?0:itos(n/BASE, b); 739 740 *(b+m) = "0123456789abcdef"[n%BASE]; 741 return m+1; 742 } 743 744 /* 745 * convert decimal ascii representation in b of length len to integer 746 */ 747 int 748 atoi(b, len) 749 char *b; 750 unsigned int len; 751 { 752 int n = 0; 753 754 while (len--) { 755 register char c = *b++; 756 if (c < '0' || c > '9') 757 return -1; 758 n = 10 * n + (c - '0'); 759 } 760 return n; 761 } 762