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