xref: /netbsd-src/sys/lib/libsa/ufs.c (revision 81b108b45f75f89f1e3ffad9fb6f074e771c0935)
1 /*	$NetBSD: ufs.c,v 1.15 1996/06/02 13:28:21 ragge Exp $	*/
2 
3 /*-
4  * Copyright (c) 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * The Mach Operating System project at Carnegie-Mellon University.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  *
39  * Copyright (c) 1990, 1991 Carnegie Mellon University
40  * All Rights Reserved.
41  *
42  * Author: David Golub
43  *
44  * Permission to use, copy, modify and distribute this software and its
45  * documentation is hereby granted, provided that both the copyright
46  * notice and this permission notice appear in all copies of the
47  * software, derivative works or modified versions, and any portions
48  * thereof, and that both notices appear in supporting documentation.
49  *
50  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
51  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
52  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
53  *
54  * Carnegie Mellon requests users of this software to return to
55  *
56  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
57  *  School of Computer Science
58  *  Carnegie Mellon University
59  *  Pittsburgh PA 15213-3890
60  *
61  * any improvements or extensions that they make and grant Carnegie the
62  * rights to redistribute these changes.
63  */
64 
65 /*
66  *	Stand-alone file reading package.
67  */
68 
69 #include <sys/param.h>
70 #include <sys/time.h>
71 #include <ufs/ffs/fs.h>
72 #include <ufs/ufs/dinode.h>
73 #include <ufs/ufs/dir.h>
74 #include <lib/libkern/libkern.h>
75 
76 #include <string.h>
77 
78 #include "stand.h"
79 
80 /*
81  * In-core open file.
82  */
83 struct file {
84 	off_t		f_seekp;	/* seek pointer */
85 	struct fs	*f_fs;		/* pointer to super-block */
86 	struct dinode	f_di;		/* copy of on-disk inode */
87 	int		f_nindir[NIADDR];
88 					/* number of blocks mapped by
89 					   indirect block at level i */
90 	char		*f_blk[NIADDR];	/* buffer for indirect block at
91 					   level i */
92 	size_t		f_blksize[NIADDR];
93 					/* size of buffer */
94 	daddr_t		f_blkno[NIADDR];/* disk address of block in buffer */
95 	char		*f_buf;		/* buffer for data block */
96 	size_t		f_buf_size;	/* size of data block */
97 	daddr_t		f_buf_blkno;	/* block number of data block */
98 };
99 
100 static int	read_inode __P((ino_t, struct open_file *));
101 static int	block_map __P((struct open_file *, daddr_t, daddr_t *));
102 static int	buf_read_file __P((struct open_file *, char **, size_t *));
103 static int	search_directory __P((char *, struct open_file *, ino_t *));
104 #ifdef COMPAT_UFS
105 static void	ffs_oldfscompat __P((struct fs *));
106 #endif
107 
108 /*
109  * Read a new inode into a file structure.
110  */
111 static int
112 read_inode(inumber, f)
113 	ino_t inumber;
114 	struct open_file *f;
115 {
116 	register struct file *fp = (struct file *)f->f_fsdata;
117 	register struct fs *fs = fp->f_fs;
118 	char *buf;
119 	size_t rsize;
120 	int rc;
121 
122 	/*
123 	 * Read inode and save it.
124 	 */
125 	buf = alloc(fs->fs_bsize);
126 	twiddle();
127 	rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
128 		fsbtodb(fs, ino_to_fsba(fs, inumber)), fs->fs_bsize,
129 		buf, &rsize);
130 	if (rc)
131 		goto out;
132 	if (rsize != fs->fs_bsize) {
133 		rc = EIO;
134 		goto out;
135 	}
136 
137 	{
138 		register struct dinode *dp;
139 
140 		dp = (struct dinode *)buf;
141 		fp->f_di = dp[ino_to_fsbo(fs, inumber)];
142 	}
143 
144 	/*
145 	 * Clear out the old buffers
146 	 */
147 	{
148 		register int level;
149 
150 		for (level = 0; level < NIADDR; level++)
151 			fp->f_blkno[level] = -1;
152 		fp->f_buf_blkno = -1;
153 	}
154 out:
155 	free(buf, fs->fs_bsize);
156 	return (rc);
157 }
158 
159 /*
160  * Given an offset in a file, find the disk block number that
161  * contains that block.
162  */
163 static int
164 block_map(f, file_block, disk_block_p)
165 	struct open_file *f;
166 	daddr_t file_block;
167 	daddr_t *disk_block_p;	/* out */
168 {
169 	register struct file *fp = (struct file *)f->f_fsdata;
170 	register struct fs *fs = fp->f_fs;
171 	int level;
172 	int idx;
173 	daddr_t ind_block_num;
174 	daddr_t *ind_p;
175 	int rc;
176 
177 	/*
178 	 * Index structure of an inode:
179 	 *
180 	 * di_db[0..NDADDR-1]	hold block numbers for blocks
181 	 *			0..NDADDR-1
182 	 *
183 	 * di_ib[0]		index block 0 is the single indirect block
184 	 *			holds block numbers for blocks
185 	 *			NDADDR .. NDADDR + NINDIR(fs)-1
186 	 *
187 	 * di_ib[1]		index block 1 is the double indirect block
188 	 *			holds block numbers for INDEX blocks for blocks
189 	 *			NDADDR + NINDIR(fs) ..
190 	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1
191 	 *
192 	 * di_ib[2]		index block 2 is the triple indirect block
193 	 *			holds block numbers for double-indirect
194 	 *			blocks for blocks
195 	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2 ..
196 	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2
197 	 *				+ NINDIR(fs)**3 - 1
198 	 */
199 
200 	if (file_block < NDADDR) {
201 		/* Direct block. */
202 		*disk_block_p = fp->f_di.di_db[file_block];
203 		return (0);
204 	}
205 
206 	file_block -= NDADDR;
207 
208 	/*
209 	 * nindir[0] = NINDIR
210 	 * nindir[1] = NINDIR**2
211 	 * nindir[2] = NINDIR**3
212 	 *	etc
213 	 */
214 	for (level = 0; level < NIADDR; level++) {
215 		if (file_block < fp->f_nindir[level])
216 			break;
217 		file_block -= fp->f_nindir[level];
218 	}
219 	if (level == NIADDR) {
220 		/* Block number too high */
221 		return (EFBIG);
222 	}
223 
224 	ind_block_num = fp->f_di.di_ib[level];
225 
226 	for (; level >= 0; level--) {
227 		if (ind_block_num == 0) {
228 			*disk_block_p = 0;	/* missing */
229 			return (0);
230 		}
231 
232 		if (fp->f_blkno[level] != ind_block_num) {
233 			if (fp->f_blk[level] == (char *)0)
234 				fp->f_blk[level] =
235 					alloc(fs->fs_bsize);
236 			twiddle();
237 			rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
238 				fsbtodb(fp->f_fs, ind_block_num),
239 				fs->fs_bsize,
240 				fp->f_blk[level],
241 				&fp->f_blksize[level]);
242 			if (rc)
243 				return (rc);
244 			if (fp->f_blksize[level] != fs->fs_bsize)
245 				return (EIO);
246 			fp->f_blkno[level] = ind_block_num;
247 		}
248 
249 		ind_p = (daddr_t *)fp->f_blk[level];
250 
251 		if (level > 0) {
252 			idx = file_block / fp->f_nindir[level - 1];
253 			file_block %= fp->f_nindir[level - 1];
254 		} else
255 			idx = file_block;
256 
257 		ind_block_num = ind_p[idx];
258 	}
259 
260 	*disk_block_p = ind_block_num;
261 
262 	return (0);
263 }
264 
265 /*
266  * Read a portion of a file into an internal buffer.  Return
267  * the location in the buffer and the amount in the buffer.
268  */
269 static int
270 buf_read_file(f, buf_p, size_p)
271 	struct open_file *f;
272 	char **buf_p;		/* out */
273 	size_t *size_p;		/* out */
274 {
275 	register struct file *fp = (struct file *)f->f_fsdata;
276 	register struct fs *fs = fp->f_fs;
277 	long off;
278 	register daddr_t file_block;
279 	daddr_t	disk_block;
280 	size_t block_size;
281 	int rc;
282 
283 	off = blkoff(fs, fp->f_seekp);
284 	file_block = lblkno(fs, fp->f_seekp);
285 	block_size = dblksize(fs, &fp->f_di, file_block);
286 
287 	if (file_block != fp->f_buf_blkno) {
288 		rc = block_map(f, file_block, &disk_block);
289 		if (rc)
290 			return (rc);
291 
292 		if (fp->f_buf == (char *)0)
293 			fp->f_buf = alloc(fs->fs_bsize);
294 
295 		if (disk_block == 0) {
296 			bzero(fp->f_buf, block_size);
297 			fp->f_buf_size = block_size;
298 		} else {
299 			twiddle();
300 			rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
301 				fsbtodb(fs, disk_block),
302 				block_size, fp->f_buf, &fp->f_buf_size);
303 			if (rc)
304 				return (rc);
305 		}
306 
307 		fp->f_buf_blkno = file_block;
308 	}
309 
310 	/*
311 	 * Return address of byte in buffer corresponding to
312 	 * offset, and size of remainder of buffer after that
313 	 * byte.
314 	 */
315 	*buf_p = fp->f_buf + off;
316 	*size_p = block_size - off;
317 
318 	/*
319 	 * But truncate buffer at end of file.
320 	 */
321 	if (*size_p > fp->f_di.di_size - fp->f_seekp)
322 		*size_p = fp->f_di.di_size - fp->f_seekp;
323 
324 	return (0);
325 }
326 
327 /*
328  * Search a directory for a name and return its
329  * i_number.
330  */
331 static int
332 search_directory(name, f, inumber_p)
333 	char *name;
334 	struct open_file *f;
335 	ino_t *inumber_p;		/* out */
336 {
337 	register struct file *fp = (struct file *)f->f_fsdata;
338 	register struct direct *dp;
339 	struct direct *edp;
340 	char *buf;
341 	size_t buf_size;
342 	int namlen, length;
343 	int rc;
344 
345 	length = strlen(name);
346 
347 	fp->f_seekp = 0;
348 	while (fp->f_seekp < fp->f_di.di_size) {
349 		rc = buf_read_file(f, &buf, &buf_size);
350 		if (rc)
351 			return (rc);
352 
353 		dp = (struct direct *)buf;
354 		edp = (struct direct *)(buf + buf_size);
355 		while (dp < edp) {
356 			if (dp->d_ino == (ino_t)0)
357 				goto next;
358 #if BYTE_ORDER == LITTLE_ENDIAN
359 			if (fp->f_fs->fs_maxsymlinklen <= 0)
360 				namlen = dp->d_type;
361 			else
362 #endif
363 				namlen = dp->d_namlen;
364 			if (namlen == length &&
365 			    !strcmp(name, dp->d_name)) {
366 				/* found entry */
367 				*inumber_p = dp->d_ino;
368 				return (0);
369 			}
370 		next:
371 			dp = (struct direct *)((char *)dp + dp->d_reclen);
372 		}
373 		fp->f_seekp += buf_size;
374 	}
375 	return (ENOENT);
376 }
377 
378 /*
379  * Open a file.
380  */
381 int
382 ufs_open(path, f)
383 	char *path;
384 	struct open_file *f;
385 {
386 	register char *cp, *ncp;
387 	register int c;
388 	ino_t inumber, parent_inumber;
389 	struct file *fp;
390 	struct fs *fs;
391 	int rc;
392 	size_t buf_size;
393 	int nlinks = 0;
394 	char namebuf[MAXPATHLEN+1];
395 	char *buf = NULL;
396 
397 	/* allocate file system specific data structure */
398 	fp = alloc(sizeof(struct file));
399 	bzero(fp, sizeof(struct file));
400 	f->f_fsdata = (void *)fp;
401 
402 	/* allocate space and read super block */
403 	fs = alloc(SBSIZE);
404 	fp->f_fs = fs;
405 	twiddle();
406 	rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
407 		SBLOCK, SBSIZE, (char *)fs, &buf_size);
408 	if (rc)
409 		goto out;
410 
411 	if (buf_size != SBSIZE || fs->fs_magic != FS_MAGIC ||
412 	    fs->fs_bsize > MAXBSIZE || fs->fs_bsize < sizeof(struct fs)) {
413 		rc = EINVAL;
414 		goto out;
415 	}
416 #ifdef COMPAT_UFS
417 	ffs_oldfscompat(fs);
418 #endif
419 
420 	/*
421 	 * Calculate indirect block levels.
422 	 */
423 	{
424 		register int mult;
425 		register int level;
426 
427 		mult = 1;
428 		for (level = 0; level < NIADDR; level++) {
429 			mult *= NINDIR(fs);
430 			fp->f_nindir[level] = mult;
431 		}
432 	}
433 
434 	inumber = ROOTINO;
435 	if ((rc = read_inode(inumber, f)) != 0)
436 		goto out;
437 
438 	cp = path;
439 	while (*cp) {
440 
441 		/*
442 		 * Remove extra separators
443 		 */
444 		while (*cp == '/')
445 			cp++;
446 		if (*cp == '\0')
447 			break;
448 
449 		/*
450 		 * Check that current node is a directory.
451 		 */
452 		if ((fp->f_di.di_mode & IFMT) != IFDIR) {
453 			rc = ENOTDIR;
454 			goto out;
455 		}
456 
457 		/*
458 		 * Get next component of path name.
459 		 */
460 		{
461 			register int len = 0;
462 
463 			ncp = cp;
464 			while ((c = *cp) != '\0' && c != '/') {
465 				if (++len > MAXNAMLEN) {
466 					rc = ENOENT;
467 					goto out;
468 				}
469 				cp++;
470 			}
471 			*cp = '\0';
472 		}
473 
474 		/*
475 		 * Look up component in current directory.
476 		 * Save directory inumber in case we find a
477 		 * symbolic link.
478 		 */
479 		parent_inumber = inumber;
480 		rc = search_directory(ncp, f, &inumber);
481 		*cp = c;
482 		if (rc)
483 			goto out;
484 
485 		/*
486 		 * Open next component.
487 		 */
488 		if ((rc = read_inode(inumber, f)) != 0)
489 			goto out;
490 
491 		/*
492 		 * Check for symbolic link.
493 		 */
494 		if ((fp->f_di.di_mode & IFMT) == IFLNK) {
495 			int link_len = fp->f_di.di_size;
496 			int len;
497 
498 			len = strlen(cp);
499 
500 			if (link_len + len > MAXPATHLEN ||
501 			    ++nlinks > MAXSYMLINKS) {
502 				rc = ENOENT;
503 				goto out;
504 			}
505 
506 			bcopy(cp, &namebuf[link_len], len + 1);
507 
508 			if (link_len < fs->fs_maxsymlinklen) {
509 				bcopy(fp->f_di.di_shortlink, namebuf,
510 				      (unsigned) link_len);
511 			} else {
512 				/*
513 				 * Read file for symbolic link
514 				 */
515 				size_t buf_size;
516 				daddr_t	disk_block;
517 				register struct fs *fs = fp->f_fs;
518 
519 				if (!buf)
520 					buf = alloc(fs->fs_bsize);
521 				rc = block_map(f, (daddr_t)0, &disk_block);
522 				if (rc)
523 					goto out;
524 
525 				twiddle();
526 				rc = (f->f_dev->dv_strategy)(f->f_devdata,
527 					F_READ, fsbtodb(fs, disk_block),
528 					fs->fs_bsize, buf, &buf_size);
529 				if (rc)
530 					goto out;
531 
532 				bcopy((char *)buf, namebuf, (unsigned)link_len);
533 			}
534 
535 			/*
536 			 * If relative pathname, restart at parent directory.
537 			 * If absolute pathname, restart at root.
538 			 */
539 			cp = namebuf;
540 			if (*cp != '/')
541 				inumber = parent_inumber;
542 			else
543 				inumber = (ino_t)ROOTINO;
544 
545 			if ((rc = read_inode(inumber, f)) != 0)
546 				goto out;
547 		}
548 	}
549 
550 	/*
551 	 * Found terminal component.
552 	 */
553 	rc = 0;
554 out:
555 	if (buf)
556 		free(buf, fs->fs_bsize);
557 	if (rc)
558 		free(fp, sizeof(struct file));
559 	return (rc);
560 }
561 
562 int
563 ufs_close(f)
564 	struct open_file *f;
565 {
566 	register struct file *fp = (struct file *)f->f_fsdata;
567 	int level;
568 
569 	f->f_fsdata = (void *)0;
570 	if (fp == (struct file *)0)
571 		return (0);
572 
573 	for (level = 0; level < NIADDR; level++) {
574 		if (fp->f_blk[level])
575 			free(fp->f_blk[level], fp->f_fs->fs_bsize);
576 	}
577 	if (fp->f_buf)
578 		free(fp->f_buf, fp->f_fs->fs_bsize);
579 	free(fp->f_fs, SBSIZE);
580 	free(fp, sizeof(struct file));
581 	return (0);
582 }
583 
584 /*
585  * Copy a portion of a file into kernel memory.
586  * Cross block boundaries when necessary.
587  */
588 int
589 ufs_read(f, start, size, resid)
590 	struct open_file *f;
591 	void *start;
592 	size_t size;
593 	size_t *resid;	/* out */
594 {
595 	register struct file *fp = (struct file *)f->f_fsdata;
596 	register size_t csize;
597 	char *buf;
598 	size_t buf_size;
599 	int rc = 0;
600 	register char *addr = start;
601 
602 	while (size != 0) {
603 		if (fp->f_seekp >= fp->f_di.di_size)
604 			break;
605 
606 		rc = buf_read_file(f, &buf, &buf_size);
607 		if (rc)
608 			break;
609 
610 		csize = size;
611 		if (csize > buf_size)
612 			csize = buf_size;
613 
614 		bcopy(buf, addr, csize);
615 
616 		fp->f_seekp += csize;
617 		addr += csize;
618 		size -= csize;
619 	}
620 	if (resid)
621 		*resid = size;
622 	return (rc);
623 }
624 
625 /*
626  * Not implemented.
627  */
628 int
629 ufs_write(f, start, size, resid)
630 	struct open_file *f;
631 	void *start;
632 	size_t size;
633 	size_t *resid;	/* out */
634 {
635 
636 	return (EROFS);
637 }
638 
639 off_t
640 ufs_seek(f, offset, where)
641 	struct open_file *f;
642 	off_t offset;
643 	int where;
644 {
645 	register struct file *fp = (struct file *)f->f_fsdata;
646 
647 	switch (where) {
648 	case SEEK_SET:
649 		fp->f_seekp = offset;
650 		break;
651 	case SEEK_CUR:
652 		fp->f_seekp += offset;
653 		break;
654 	case SEEK_END:
655 		fp->f_seekp = fp->f_di.di_size - offset;
656 		break;
657 	default:
658 		return (-1);
659 	}
660 	return (fp->f_seekp);
661 }
662 
663 int
664 ufs_stat(f, sb)
665 	struct open_file *f;
666 	struct stat *sb;
667 {
668 	register struct file *fp = (struct file *)f->f_fsdata;
669 
670 	/* only important stuff */
671 	sb->st_mode = fp->f_di.di_mode;
672 	sb->st_uid = fp->f_di.di_uid;
673 	sb->st_gid = fp->f_di.di_gid;
674 	sb->st_size = fp->f_di.di_size;
675 	return (0);
676 }
677 
678 #ifdef COMPAT_UFS
679 /*
680  * Sanity checks for old file systems.
681  *
682  * XXX - goes away some day.
683  */
684 static void
685 ffs_oldfscompat(fs)
686 	struct fs *fs;
687 {
688 	int i;
689 
690 	fs->fs_npsect = max(fs->fs_npsect, fs->fs_nsect);	/* XXX */
691 	fs->fs_interleave = max(fs->fs_interleave, 1);		/* XXX */
692 	if (fs->fs_postblformat == FS_42POSTBLFMT)		/* XXX */
693 		fs->fs_nrpos = 8;				/* XXX */
694 	if (fs->fs_inodefmt < FS_44INODEFMT) {			/* XXX */
695 		quad_t sizepb = fs->fs_bsize;			/* XXX */
696 								/* XXX */
697 		fs->fs_maxfilesize = fs->fs_bsize * NDADDR - 1;	/* XXX */
698 		for (i = 0; i < NIADDR; i++) {			/* XXX */
699 			sizepb *= NINDIR(fs);			/* XXX */
700 			fs->fs_maxfilesize += sizepb;		/* XXX */
701 		}						/* XXX */
702 		fs->fs_qbmask = ~fs->fs_bmask;			/* XXX */
703 		fs->fs_qfmask = ~fs->fs_fmask;			/* XXX */
704 	}							/* XXX */
705 }
706 #endif
707