procfs_vnops.c revision 1.8 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.8 1993/12/18 03:58:14 mycroft Exp $
31 */
32
33 /*
34 * PROCFS vnode interface routines
35 */
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/time.h>
40 #include <sys/kernel.h>
41 #include <sys/ioctl.h>
42 #include <sys/file.h>
43 #include <sys/proc.h>
44 #include <sys/buf.h>
45 #include <sys/vnode.h>
46 #include <sys/namei.h>
47 #include <sys/resourcevar.h>
48 #include <vm/vm.h>
49 #include <sys/kinfo.h>
50 #include <sys/kinfo_proc.h>
51
52 #include <sys/procfs.h>
53 #include <miscfs/procfs/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