]> git.saurik.com Git - apple/boot.git/blob - gen/libsaio/sys.c
boot-111.tar.gz
[apple/boot.git] / gen / libsaio / sys.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 * Mach Operating System
27 * Copyright (c) 1990 Carnegie-Mellon University
28 * Copyright (c) 1989 Carnegie-Mellon University
29 * Copyright (c) 1988 Carnegie-Mellon University
30 * Copyright (c) 1987 Carnegie-Mellon University
31 * All rights reserved. The CMU software License Agreement specifies
32 * the terms and conditions for use and redistribution.
33 *
34 */
35 /*
36 * HISTORY
37 * Revision 2.3 88/08/08 13:47:07 rvb
38 * Allocate buffers dynamically vs statically.
39 * Now b[i] and i_fs and i_buf, are allocated dynamically.
40 * boot_calloc(size) allocates and zeros a buffer rounded to a NPG
41 * boundary.
42 * Generalize boot spec to allow, xx()/mach, xx(n,[a..h])/mach,
43 * xx([a..h])/mach, ...
44 * Also default "xx" if unspecified and alloc just "/mach",
45 * where everything is defaulted
46 * Add routine, ptol(), to parse partition letters.
47 *
48 */
49
50 /*
51 * Copyright (c) 1982, 1986 Regents of the University of California.
52 * All rights reserved. The Berkeley software License Agreement
53 * specifies the terms and conditions for redistribution.
54 *
55 * @(#)sys.c 7.1 (Berkeley) 6/5/86
56 */
57
58 #import <sys/param.h>
59 #import <ufs/fs.h>
60 #import <ufs/fsdir.h>
61 #import <sys/reboot.h>
62 #import <libkern/OSByteOrder.h>
63 #import "libsaio.h"
64 #import "cache.h"
65 #import "kernBootStruct.h"
66
67 char *gFilename;
68
69 static ino_t dlook(char *s, struct iob *io);
70 static char * xx(char *str, struct iob *file);
71 volatile void stop(char *s);
72 static int ffs(register long mask);
73
74 extern int label_secsize;
75
76 #define DCACHE 1
77 #define ICACHE 1
78 #define SYS_MESSAGES 1
79 #define CHECK_CAREFULLY 0
80
81 #if ICACHE
82 #define ICACHE_SIZE 8
83 static cache_t *icache;
84 #endif ICACHE
85
86 #define DEV_BSIZE label_secsize
87
88 static struct iob *iob_from_fdesc(int fdesc)
89 {
90 register struct iob *file;
91
92 if (fdesc < 0 || fdesc >= NFILES ||
93 ((file = &iob[fdesc])->i_flgs & F_ALLOC) == 0)
94 return NULL;
95 else
96 return file;
97 }
98
99 static int
100 openi(int n, struct iob *io)
101 {
102 struct dinode *dp;
103 int cc;
104 #if ICACHE
105 struct icommon *ip;
106
107 if (icache == 0) {
108 icache = cacheInit(ICACHE_SIZE, sizeof(struct icommon));
109 }
110 #endif ICACHE
111 io->i_offset = 0;
112 io->i_bn = fsbtodb(io->i_fs, itod(io->i_fs, n)) + io->i_boff;
113 io->i_cc = io->i_fs->fs_bsize;
114 io->i_ma = io->i_buf;
115
116 #if ICACHE
117 if (cacheFind(icache, n, 0, (char **)&ip) == 1) {
118 io->i_ino.i_ic = *ip;
119 cc = 0;
120 } else {
121 #endif ICACHE
122 cc = devread(io);
123 dp = (struct dinode *)io->i_buf;
124 #if BIG_ENDIAN_FS
125 byte_swap_inode_in(&dp[itoo(io->i_fs, n)].di_ic, &io->i_ino.i_ic);
126 #else
127 io->i_ino.i_ic = dp[itoo(io->i_fs, n)].di_ic;
128 #endif BIG_ENDIAN_FS
129 #if ICACHE
130 *ip = io->i_ino.i_ic;
131 }
132 #endif ICACHE
133 io->i_ino.i_number = n;
134 return (cc);
135 }
136
137 static int
138 find(char *path, struct iob *file)
139 {
140 char *q;
141 char c;
142 int n = 0;
143
144 #if CHECK_CAREFULLY
145 if (path==NULL || *path=='\0') {
146 printf("null path\n");
147 return (0);
148 }
149 #endif CHECK_CAREFULLY
150 if (openi((ino_t) ROOTINO, file) < 0)
151 {
152 #if SYS_MESSAGES
153 error("bad root inode\n");
154 #endif
155 return (0);
156 }
157 while (*path)
158 {
159 while (*path == '/')
160 path++;
161 q = path;
162 while(*q != '/' && *q != '\0')
163 q++;
164 c = *q;
165 *q = '\0';
166 if (q == path) path = "." ; /* "/" means "/." */
167
168 if ((n = dlook(path, file)) != 0)
169 {
170 if (c == '\0')
171 break;
172 if (openi(n, file) < 0)
173 {
174 *q = c;
175 return (0);
176 }
177 *q = c;
178 path = q;
179 continue;
180 }
181 else
182 {
183 *q = c;
184 return (0);
185 }
186 }
187 return (n);
188 }
189
190 static daddr_t
191 sbmap(struct iob *io, daddr_t bn)
192 {
193 register struct inode *ip;
194 int i, j, sh;
195 daddr_t nb, *bap;
196
197 ip = &io->i_ino;
198
199 if (ip->i_icflags & IC_FASTLINK)
200 {
201 error("fast symlinks unimplemented\n");
202 return ((daddr_t)0);
203 }
204
205 if (bn < 0) {
206 #if SYS_MESSAGES
207 error("bn negative\n");
208 #endif
209 return ((daddr_t)0);
210 }
211
212 /*
213 * blocks 0..NDADDR are direct blocks
214 */
215 if(bn < NDADDR)
216 {
217 nb = ip->i_db[bn];
218 return (nb);
219 }
220
221 /*
222 * addresses NIADDR have single and double indirect blocks.
223 * the first step is to determine how many levels of indirection.
224 */
225 sh = 1;
226 bn -= NDADDR;
227 for (j = NIADDR; j > 0; j--) {
228 sh *= NINDIR(io->i_fs);
229 if (bn < sh)
230 break;
231 bn -= sh;
232 }
233 if (j == 0) {
234 #if SYS_MESSAGES
235 error("bn ovf %d\n", bn);
236 #endif
237 return ((daddr_t)0);
238 }
239
240 /*
241 * fetch the first indirect block address from the inode
242 */
243 nb = ip->i_ib[NIADDR - j];
244 if (nb == 0) {
245 #if SYS_MESSAGES
246 error("bn void %d\n",bn);
247 #endif
248 return ((daddr_t)0);
249 }
250
251 /*
252 * fetch through the indirect blocks
253 */
254 for (; j <= NIADDR; j++) {
255 if (blknos[j] != nb) {
256 io->i_bn = fsbtodb(io->i_fs, nb) + io->i_boff;
257 if (b[j] == (char *)0)
258 b[j] = malloc(MAXBSIZE);
259 io->i_ma = b[j];
260 io->i_cc = io->i_fs->fs_bsize;
261 if (devread(io) != io->i_fs->fs_bsize) {
262 #if SYS_MESSAGES
263 error("bn %d: read error\n", io->i_bn);
264 #endif
265 return ((daddr_t)0);
266 }
267 blknos[j] = nb;
268 }
269 bap = (daddr_t *)b[j];
270 sh /= NINDIR(io->i_fs);
271 i = (bn / sh) % NINDIR(io->i_fs);
272 #if BIG_ENDIAN_FS
273 #if 1
274 // for now it is little endian FS for intel
275 nb = bap[i];
276 #else
277 nb = OSSwapBigToHostInt32(bap[i]);
278 #endif 1
279 #else BIG_ENDIAN_FS
280 nb = bap[i];
281 #endif BIG_ENDIAN_FS
282 if(nb == 0) {
283 #if SYS_MESSAGES
284 error("bn void %d\n",bn);
285 #endif
286 return ((daddr_t)0);
287 }
288 }
289
290 return (nb);
291 }
292
293 static ino_t
294 dlook(
295 char *s,
296 struct iob *io
297 )
298 {
299 struct direct *dp;
300 register struct inode *ip;
301 struct dirstuff dirp;
302 int len;
303
304 if (s == NULL || *s == '\0')
305 return (0);
306 ip = &io->i_ino;
307 if ((ip->i_mode & IFMT) != IFDIR) {
308 #if SYS_MESSAGES
309 error(". before %s not a dir\n", s);
310 #endif
311 return (0);
312 }
313 if (ip->i_size == 0) {
314 #if SYS_MESSAGES
315 error("%s: 0 length dir\n", s);
316 #endif
317 return (0);
318 }
319 len = strlen(s);
320 dirp.loc = 0;
321 dirp.io = io;
322 io->dirbuf_blkno = -1;
323
324 for (dp = readdir(&dirp); dp != NULL; dp = readdir(&dirp)) {
325 #if DEBUG1
326 printf("checking name %s\n", dp->d_name);
327 #endif DEBUG1
328 if(dp->d_ino == 0)
329 continue;
330 if (dp->d_namlen == len && !strcmp(s, dp->d_name))
331 return (dp->d_ino);
332 }
333 return (0);
334 }
335
336 struct dirstuff *
337 opendir(char *path)
338 {
339 register struct dirstuff *dirp;
340 register int fd;
341
342 dirp = (struct dirstuff *)malloc(sizeof(struct dirstuff));
343 if (dirp == (struct dirstuff *)-1)
344 return 0;
345 fd = open(path,0);
346 if (fd == -1) {
347 free((void *)dirp);
348 return 0;
349 }
350 dirp->io = &iob[fd];
351 dirp->loc = 0;
352 iob[fd].dirbuf_blkno = -1;
353 return dirp;
354 }
355
356 int
357 closedir(struct dirstuff *dirp)
358 {
359 close(iob - dirp->io);
360 free((void *)dirp);
361 return 0;
362 }
363
364 #if DCACHE
365 static cache_t *dcache;
366 #define DCACHE_SIZE 6
367 #endif
368
369 /*
370 * get next entry in a directory.
371 */
372 struct direct *
373 readdir(struct dirstuff *dirp)
374 {
375 struct direct *dp;
376 register struct iob *io;
377 daddr_t lbn, d;
378 int off;
379 #if DCACHE
380 char *bp;
381 int dirblkno;
382
383 if (dcache == 0)
384 dcache = cacheInit(DCACHE_SIZE, DIRBLKSIZ);
385 #endif DCACHE
386 io = dirp->io;
387 for(;;)
388 {
389 if (dirp->loc >= io->i_ino.i_size)
390 return (NULL);
391 off = blkoff(io->i_fs, dirp->loc);
392 lbn = lblkno(io->i_fs, dirp->loc);
393
394 #if DCACHE
395 dirblkno = dirp->loc / DIRBLKSIZ;
396 if (cacheFind(dcache, io->i_ino.i_number, dirblkno, &bp)) {
397 dp = (struct direct *)(bp + (dirp->loc % DIRBLKSIZ));
398 } else
399 #else DCACHE
400 if (io->dirbuf_blkno != lbn)
401 #endif DCACHE
402 {
403 if((d = sbmap(io, lbn)) == 0)
404 return NULL;
405 io->i_bn = fsbtodb(io->i_fs, d) + io->i_boff;
406 io->i_ma = io->i_buf;
407 io->i_cc = blksize(io->i_fs, &io->i_ino, lbn);
408
409 if (devread(io) < 0)
410 {
411 #if SYS_MESSAGES
412 error("bn %d: directory read error\n",
413 io->i_bn);
414 #endif
415 return (NULL);
416 }
417 #if BIG_ENDIAN_FS
418 byte_swap_dir_block_in(io->i_buf, io->i_cc);
419 #endif BIG_ENDIAN_FS
420 #if DCACHE
421 bcopy(io->i_buf + dirblkno * DIRBLKSIZ, bp, DIRBLKSIZ);
422 dp = (struct direct *)(io->i_buf + off);
423 #endif
424 }
425 #if !DCACHE
426 dp = (struct direct *)(io->i_buf + off);
427 #endif
428 dirp->loc += dp->d_reclen;
429
430 if (dp->d_ino != 0) return (dp);
431 }
432 }
433
434 int
435 b_lseek(int fdesc, unsigned int addr, int ptr)
436 {
437 register struct iob *io;
438
439 #if CHECK_CAREFULLY
440 if (ptr != 0) {
441 error("Seek not from beginning of file\n");
442 return (-1);
443 }
444 #endif CHECK_CAREFULLY
445 if ((io = iob_from_fdesc(fdesc)) == 0) {
446 return (-1);
447 }
448 io->i_offset = addr;
449 io->i_bn = addr / DEV_BSIZE;
450 io->i_cc = 0;
451 return (0);
452 }
453
454 int
455 tell(int fdesc)
456 {
457 return iob[fdesc].i_offset;
458 }
459
460 static int getch(int fdesc)
461 {
462 register struct iob *io;
463 struct fs *fs;
464 char *p;
465 int c, lbn, off, size, diff;
466
467 if ((io = iob_from_fdesc(fdesc)) == 0) {
468 return (-1);
469 }
470 p = io->i_ma;
471 if (io->i_cc <= 0) {
472 if ((io->i_flgs & F_FILE) != 0) {
473 diff = io->i_ino.i_size - io->i_offset;
474 if (diff <= 0)
475 return (-1);
476 fs = io->i_fs;
477 lbn = lblkno(fs, io->i_offset);
478 io->i_bn = fsbtodb(fs, sbmap(io, lbn)) + io->i_boff;
479 off = blkoff(fs, io->i_offset);
480 size = blksize(fs, &io->i_ino, lbn);
481 } else {
482 diff = 0;
483 #ifndef SMALL
484 io->i_bn = io->i_offset / DEV_BSIZE;
485 off = 0;
486 size = DEV_BSIZE;
487 #endif SMALL
488 }
489 io->i_ma = io->i_buf;
490 io->i_cc = size;
491 if (devread(io) < 0) {
492 return (-1);
493 }
494 if ((io->i_flgs & F_FILE) != 0) {
495 if (io->i_offset - off + size >= io->i_ino.i_size)
496 io->i_cc = diff + off;
497 io->i_cc -= off;
498 }
499 p = &io->i_buf[off];
500 }
501 io->i_cc--;
502 io->i_offset++;
503 c = (unsigned)*p++;
504 io->i_ma = p;
505 return (c);
506 }
507
508 int
509 read(int fdesc, char *buf, int count)
510 {
511 int i, size;
512 register struct iob *file;
513 struct fs *fs;
514 int lbn, off;
515
516 if ((file = iob_from_fdesc(fdesc)) == 0) {
517 return (-1);
518 }
519 if ((file->i_flgs&F_READ) == 0) {
520 return (-1);
521 }
522 #ifndef SMALL
523 if ((file->i_flgs & F_FILE) == 0) {
524 file->i_cc = count;
525 file->i_ma = buf;
526 file->i_bn = file->i_boff + (file->i_offset / DEV_BSIZE);
527 i = devread(file);
528 file->i_offset += count;
529 return (i);
530 }
531 #endif SMALL
532 if (file->i_offset+count > file->i_ino.i_size)
533 count = file->i_ino.i_size - file->i_offset;
534 if ((i = count) <= 0)
535 return (0);
536 /*
537 * While reading full blocks, do I/O into user buffer.
538 * Anything else uses getc().
539 */
540 fs = file->i_fs;
541 while (i) {
542 off = blkoff(fs, file->i_offset);
543 lbn = lblkno(fs, file->i_offset);
544 size = blksize(fs, &file->i_ino, lbn);
545 if (off == 0 && size <= i) {
546 file->i_bn = fsbtodb(fs, sbmap(file, lbn)) +
547 file->i_boff;
548 file->i_cc = size;
549 file->i_ma = buf;
550 if (devread(file) < 0) {
551 return (-1);
552 }
553 file->i_offset += size;
554 file->i_cc = 0;
555 buf += size;
556 i -= size;
557 } else {
558 size -= off;
559 if (size > i)
560 size = i;
561 i -= size;
562 do {
563 *buf++ = getch(fdesc);
564 } while (--size);
565 }
566 }
567 return (count);
568 }
569
570 #if CHECK_CAREFULLY
571 static int open_init;
572 #endif CHECK_CAREFULLY
573 static struct fs *fs_block;
574 static int fs_block_valid;
575
576 #define SUPERBLOCK_ERROR "Bad superblock: error %d\n"
577
578 int
579 open(char *str, int how)
580 {
581 register char *cp;
582 register struct iob *file;
583 int i, fdesc;
584
585 #if CHECK_CAREFULLY /* iob[] is in BSS, so it is guaranteed to be zero. */
586 if (open_init == 0) {
587 for (i = 0; i < NFILES; i++)
588 iob[i].i_flgs = 0;
589 open_init = 1;
590 }
591 #endif
592
593 for (fdesc = 0; fdesc < NFILES; fdesc++)
594 if (iob[fdesc].i_flgs == 0)
595 goto gotfile;
596 stop("Out of file descriptor slots");
597
598 gotfile:
599 (file = &iob[fdesc])->i_flgs |= F_ALLOC;
600
601 if ((cp = xx(str, file)) == (char *) -1)
602 {
603 close(fdesc);
604 return -1;
605 }
606
607 if (*cp == '\0') {
608 file->i_flgs |= how+1;
609 file->i_cc = 0;
610 file->i_offset = 0;
611 return (fdesc);
612 }
613 file->i_cc = SBSIZE;
614 file->i_bn = (SBLOCK / DEV_BSIZE) + file->i_boff;
615 file->i_offset = 0;
616
617 if (file->i_fs == 0) {
618 if (fs_block == 0) {
619 fs_block = (struct fs *)malloc(SBSIZE);
620 }
621 if (fs_block_valid == 0) {
622 file->i_ma = (char *)fs_block;
623 if (devread(file) < 0) {
624 #ifndef SMALL
625 error(SUPERBLOCK_ERROR, 1);
626 #endif
627 close(fdesc);
628 return (-1);
629 }
630 byte_swap_superblock(fs_block);
631 fs_block_valid = 1;
632 }
633 file->i_fs = fs_block;
634 file->i_buf = malloc(MAXBSIZE);
635 }
636 #if BIG_ENDIAN_FS
637
638 if (file->i_fs->fs_magic != FS_MAGIC) {
639 error(SUPERBLOCK_ERROR, 2);
640 close(fdesc);
641 return (-1);
642 }
643 /*
644 * The following is a gross hack to boot disks that have an actual
645 * blocksize of 512 bytes but were written with a theoretical 1024
646 * byte blocksize (fsbtodb == 0).
647 *
648 * We can make this assumption because we can only boot disks with
649 * a 512 byte sector size.
650 */
651 if (file->i_fs->fs_fsize == 0) {
652 error(SUPERBLOCK_ERROR,3);
653 close(fdesc);
654 return (-1);
655 }
656 file->i_fs->fs_fsbtodb = ffs(file->i_fs->fs_fsize / DEV_BSIZE) - 1;
657 #endif BIG_ENDIAN_FS
658
659 if ((i = find(cp, file)) == 0) {
660 close(fdesc);
661 return (-1);
662 }
663 #if CHECK_CAREFULLY
664 if (how != 0) {
665 error("Can't write files\n");
666 close(fdesc);
667 return (-1);
668 }
669 #endif CHECK_CAREFULLY
670
671 if (openi(i, file) < 0) {
672 close(fdesc);
673 return (-1);
674 }
675 file->i_offset = 0;
676 file->i_cc = 0;
677 file->i_flgs |= F_FILE | (how+1);
678
679 return (fdesc);
680 }
681
682 #define LP '('
683 #define RP ')'
684
685 static char * xx(char *str, struct iob *file)
686 {
687 register char *cp = str, *xp;
688 char **dp;
689 int old_dev = kernBootStruct->kernDev;
690 int dev = (kernBootStruct->kernDev >> B_TYPESHIFT) & B_TYPEMASK;
691 int unit = (kernBootStruct->kernDev >> B_UNITSHIFT) & B_UNITMASK;
692 int part = (kernBootStruct->kernDev >> B_PARTITIONSHIFT) &
693 B_PARTITIONMASK;
694 int i;
695 int no_dev;
696 int biosOffset;
697
698 biosOffset = unit; // set the device
699
700 for (; *cp && *cp != LP; cp++) ;
701 if (no_dev = !*cp) { // no paren found
702 cp = str;
703 xp = devsw[dev];
704 } else if (cp == str) { // paren but no device
705 cp++;
706 xp = devsw[dev];
707 } else {
708 xp = str;
709 cp++;
710 }
711
712 for (dp = devsw; *dp; dp++)
713 {
714 if ((xp[0] == *dp[0]) && (xp[1] == *(dp[0] + 1)))
715 goto gotdev;
716 }
717
718 error("Unknown device '%c%c'\n",xp[0],xp[1]);
719 return ((char *)-1);
720
721 gotdev:
722 if (no_dev)
723 goto none;
724 i = 0;
725 while (*cp >= '0' && *cp <= '9')
726 {
727 i = i * 10 + *cp++ - '0';
728 unit = i;
729 }
730
731 biosOffset = unit; // set the device
732
733 if (*cp == RP || no_dev)
734 /* do nothing since ptol(")") returns 0 */ ;
735 else if (*cp == ',' )
736 part = ptol(++cp);
737 else if (cp[-1] == LP)
738 part = ptol(cp);
739 else {
740 badoff:
741 error("Missing offset specification\n");
742 return ((char *)-1);
743 }
744
745 for ( ;!no_dev ;) {
746 if (*cp == RP)
747 break;
748 if (*cp++)
749 continue;
750 goto badoff;
751 }
752
753 none:
754 file->i_ino.i_dev = dev = dp-devsw;
755 file->partition = part;
756 file->biosdev = (BIOSDEV(dev)) + biosOffset;
757 if (dev == DEV_SD) {
758 file->biosdev += kernBootStruct->numIDEs;
759 } else if (dev == DEV_HD && kernBootStruct->numIDEs == 0) {
760 error("No IDE drives detected\n");
761 return ((char *)-1);
762 }
763
764 kernBootStruct->kernDev = (dev << B_TYPESHIFT) |
765 (unit << B_UNITSHIFT) |
766 (part << B_PARTITIONSHIFT);
767
768 if (kernBootStruct->kernDev != old_dev)
769 flushdev();
770
771 devopen(str, file);
772
773 if (file->i_error)
774 return (char *)-1;
775 if (!no_dev && *cp) cp++;
776
777 gFilename = cp;
778
779 return cp;
780 }
781
782 int
783 close(int fdesc)
784 {
785 register struct iob *file;
786 register int i;
787
788 if ((file = iob_from_fdesc(fdesc)) == 0) {
789 return (-1);
790 }
791 // free((char *)file->i_fs);
792 file->i_fs = NULL;
793 free(file->i_buf); file->i_buf = NULL;
794 for (i=0;i<NBUFS;i++)
795 {
796 if (b[i])
797 { free(b[i]);
798 b[i] = NULL;
799 }
800 blknos[i] = 0;
801 }
802
803 file->i_flgs = 0;
804 return (0);
805 }
806
807 volatile void stop(char *s)
808 {
809 #if CHECK_CAREFULLY
810 register int i;
811
812 for (i = 0; i < NFILES; i++)
813 if (iob[i].i_flgs != 0)
814 close(i);
815 #endif CHECK_CAREFULLY
816 /* textMode();*/ // can't call this function from here
817 printf("\n%s\n", s);
818 sleep(4); // about to halt
819 halt();
820 }
821
822 static int ffs(register long mask)
823 {
824 register int cnt;
825
826 if (mask == 0) return(0);
827 for (cnt = 1; !(mask & 1); cnt++)
828 mask >>= 1;
829 return(cnt);
830 }
831
832 int file_size(int fdesc)
833 {
834 register struct iob *io;
835
836 if ((io = iob_from_fdesc(fdesc)) == 0) {
837 return (-1);
838 }
839 return io->i_ino.i_size;
840 }
841
842 /* ensure that all device caches are flushed,
843 * because we are about to change the device media
844 */
845
846 void flushdev(void)
847 {
848 register int i;
849
850 devflush();
851 for (i = 0; i < NFILES; i++)
852 if (iob[i].i_flgs & (F_READ | F_WRITE))
853 printf("flushdev: fd %d is open\n",i);
854 fs_block_valid = 0;
855 #if ICACHE
856 cacheFlush(icache);
857 #endif
858 #if DCACHE
859 cacheFlush(dcache);
860 #endif
861 }
862