xref: /netbsd-src/sys/lib/libsa/ufs.c (revision ae9172d6cd9432a6a1a56760d86b32c57a66c39c)
1 /*	$NetBSD: ufs.c,v 1.8 1994/10/26 05:45:09 cgd 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 <string.h>
70 #include <sys/param.h>
71 #include <sys/time.h>
72 #include <ufs/ffs/fs.h>
73 #include <ufs/ufs/dinode.h>
74 #include <ufs/ufs/dir.h>
75 #include <lib/libkern/libkern.h>
76 
77 #include "stand.h"
78 
79 /*
80  * In-core open file.
81  */
82 struct file {
83 	off_t		f_seekp;	/* seek pointer */
84 	struct fs	*f_fs;		/* pointer to super-block */
85 	struct dinode	f_di;		/* copy of on-disk inode */
86 	int		f_nindir[NIADDR];
87 					/* number of blocks mapped by
88 					   indirect block at level i */
89 	char		*f_blk[NIADDR];	/* buffer for indirect block at
90 					   level i */
91 	u_long		f_blksize[NIADDR];
92 					/* size of buffer */
93 	daddr_t		f_blkno[NIADDR];/* disk address of block in buffer */
94 	char		*f_buf;		/* buffer for data block */
95 	u_int		f_buf_size;	/* size of data block */
96 	daddr_t		f_buf_blkno;	/* block number of data block */
97 };
98 
99 #ifdef COMPAT_UFS
100 void ffs_oldfscompat __P((struct fs *));
101 #endif
102 
103 /*
104  * Read a new inode into a file structure.
105  */
106 static int
107 read_inode(inumber, f)
108 	ino_t inumber;
109 	struct open_file *f;
110 {
111 	register struct file *fp = (struct file *)f->f_fsdata;
112 	register struct fs *fs = fp->f_fs;
113 	char *buf;
114 	u_int rsize;
115 	int rc;
116 
117 	/*
118 	 * Read inode and save it.
119 	 */
120 	buf = alloc(fs->fs_bsize);
121 	rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
122 		fsbtodb(fs, ino_to_fsba(fs, inumber)), fs->fs_bsize, buf, &rsize);
123 	if (rc)
124 		goto out;
125 	if (rsize != fs->fs_bsize) {
126 		rc = EIO;
127 		goto out;
128 	}
129 
130 	{
131 		register struct dinode *dp;
132 
133 		dp = (struct dinode *)buf;
134 		fp->f_di = dp[ino_to_fsbo(fs, inumber)];
135 	}
136 
137 	/*
138 	 * Clear out the old buffers
139 	 */
140 	{
141 		register int level;
142 
143 		for (level = 0; level < NIADDR; level++)
144 			fp->f_blkno[level] = -1;
145 		fp->f_buf_blkno = -1;
146 	}
147 out:
148 	free(buf, fs->fs_bsize);
149 	return (0);
150 }
151 
152 /*
153  * Given an offset in a file, find the disk block number that
154  * contains that block.
155  */
156 static int
157 block_map(f, file_block, disk_block_p)
158 	struct open_file *f;
159 	daddr_t file_block;
160 	daddr_t *disk_block_p;	/* out */
161 {
162 	register struct file *fp = (struct file *)f->f_fsdata;
163 	register struct fs *fs = fp->f_fs;
164 	int level;
165 	int idx;
166 	daddr_t ind_block_num;
167 	daddr_t *ind_p;
168 	int rc;
169 
170 	/*
171 	 * Index structure of an inode:
172 	 *
173 	 * di_db[0..NDADDR-1]	hold block numbers for blocks
174 	 *			0..NDADDR-1
175 	 *
176 	 * di_ib[0]		index block 0 is the single indirect block
177 	 *			holds block numbers for blocks
178 	 *			NDADDR .. NDADDR + NINDIR(fs)-1
179 	 *
180 	 * di_ib[1]		index block 1 is the double indirect block
181 	 *			holds block numbers for INDEX blocks for blocks
182 	 *			NDADDR + NINDIR(fs) ..
183 	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1
184 	 *
185 	 * di_ib[2]		index block 2 is the triple indirect block
186 	 *			holds block numbers for double-indirect
187 	 *			blocks for blocks
188 	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2 ..
189 	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2
190 	 *				+ NINDIR(fs)**3 - 1
191 	 */
192 
193 	if (file_block < NDADDR) {
194 		/* Direct block. */
195 		*disk_block_p = fp->f_di.di_db[file_block];
196 		return (0);
197 	}
198 
199 	file_block -= NDADDR;
200 
201 	/*
202 	 * nindir[0] = NINDIR
203 	 * nindir[1] = NINDIR**2
204 	 * nindir[2] = NINDIR**3
205 	 *	etc
206 	 */
207 	for (level = 0; level < NIADDR; level++) {
208 		if (file_block < fp->f_nindir[level])
209 			break;
210 		file_block -= fp->f_nindir[level];
211 	}
212 	if (level == NIADDR) {
213 		/* Block number too high */
214 		return (EFBIG);
215 	}
216 
217 	ind_block_num = fp->f_di.di_ib[level];
218 
219 	for (; level >= 0; level--) {
220 		if (ind_block_num == 0) {
221 			*disk_block_p = 0;	/* missing */
222 			return (0);
223 		}
224 
225 		if (fp->f_blkno[level] != ind_block_num) {
226 			if (fp->f_blk[level] == (char *)0)
227 				fp->f_blk[level] =
228 					alloc(fs->fs_bsize);
229 			rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
230 				fsbtodb(fp->f_fs, ind_block_num),
231 				fs->fs_bsize,
232 				fp->f_blk[level],
233 				(u_int *)&fp->f_blksize[level]);
234 			if (rc)
235 				return (rc);
236 			if (fp->f_blksize[level] != fs->fs_bsize)
237 				return (EIO);
238 			fp->f_blkno[level] = ind_block_num;
239 		}
240 
241 		ind_p = (daddr_t *)fp->f_blk[level];
242 
243 		if (level > 0) {
244 			idx = file_block / fp->f_nindir[level - 1];
245 			file_block %= fp->f_nindir[level - 1];
246 		} else
247 			idx = file_block;
248 
249 		ind_block_num = ind_p[idx];
250 	}
251 
252 	*disk_block_p = ind_block_num;
253 
254 	return (0);
255 }
256 
257 /*
258  * Read a portion of a file into an internal buffer.  Return
259  * the location in the buffer and the amount in the buffer.
260  */
261 static int
262 buf_read_file(f, buf_p, size_p)
263 	struct open_file *f;
264 	char **buf_p;		/* out */
265 	u_int *size_p;		/* out */
266 {
267 	register struct file *fp = (struct file *)f->f_fsdata;
268 	register struct fs *fs = fp->f_fs;
269 	long off;
270 	register daddr_t file_block;
271 	daddr_t	disk_block;
272 	long block_size;
273 	int rc;
274 
275 	off = blkoff(fs, fp->f_seekp);
276 	file_block = lblkno(fs, fp->f_seekp);
277 	block_size = dblksize(fs, &fp->f_di, file_block);
278 
279 	if (file_block != fp->f_buf_blkno) {
280 		rc = block_map(f, file_block, &disk_block);
281 		if (rc)
282 			return (rc);
283 
284 		if (fp->f_buf == (char *)0)
285 			fp->f_buf = alloc(fs->fs_bsize);
286 
287 		if (disk_block == 0) {
288 			bzero(fp->f_buf, block_size);
289 			fp->f_buf_size = block_size;
290 		} else {
291 			rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
292 				fsbtodb(fs, disk_block),
293 				block_size, fp->f_buf, &fp->f_buf_size);
294 			if (rc)
295 				return (rc);
296 		}
297 
298 		fp->f_buf_blkno = file_block;
299 	}
300 
301 	/*
302 	 * Return address of byte in buffer corresponding to
303 	 * offset, and size of remainder of buffer after that
304 	 * byte.
305 	 */
306 	*buf_p = fp->f_buf + off;
307 	*size_p = block_size - off;
308 
309 	/*
310 	 * But truncate buffer at end of file.
311 	 */
312 	if (*size_p > fp->f_di.di_size - fp->f_seekp)
313 		*size_p = fp->f_di.di_size - fp->f_seekp;
314 
315 	return (0);
316 }
317 
318 /*
319  * Search a directory for a name and return its
320  * i_number.
321  */
322 static int
323 search_directory(name, f, inumber_p)
324 	char *name;
325 	struct open_file *f;
326 	ino_t *inumber_p;		/* out */
327 {
328 	register struct file *fp = (struct file *)f->f_fsdata;
329 	register struct direct *dp;
330 	struct direct *edp;
331 	char *buf;
332 	u_int buf_size;
333 	int namlen, length;
334 	int rc;
335 
336 	length = strlen(name);
337 
338 	fp->f_seekp = 0;
339 	while (fp->f_seekp < fp->f_di.di_size) {
340 		rc = buf_read_file(f, &buf, &buf_size);
341 		if (rc)
342 			return (rc);
343 
344 		dp = (struct direct *)buf;
345 		edp = (struct direct *)(buf + buf_size);
346 		while (dp < edp) {
347 			if (dp->d_ino == (ino_t)0)
348 				goto next;
349 #if BYTE_ORDER == LITTLE_ENDIAN
350 			if (fp->f_fs->fs_maxsymlinklen <= 0)
351 				namlen = dp->d_type;
352 			else
353 #endif
354 				namlen = dp->d_namlen;
355 			if (namlen == length &&
356 			    !strcmp(name, dp->d_name)) {
357 				/* found entry */
358 				*inumber_p = dp->d_ino;
359 				return (0);
360 			}
361 		next:
362 			dp = (struct direct *)((char *)dp + dp->d_reclen);
363 		}
364 		fp->f_seekp += buf_size;
365 	}
366 	return (ENOENT);
367 }
368 
369 /*
370  * Open a file.
371  */
372 int
373 ufs_open(path, f)
374 	char *path;
375 	struct open_file *f;
376 {
377 	register char *cp, *ncp;
378 	register int c;
379 	ino_t inumber, parent_inumber;
380 	struct file *fp;
381 	struct fs *fs;
382 	int rc;
383 	u_int buf_size;
384 #if 0
385 	int nlinks = 0;
386 	char namebuf[MAXPATHLEN+1];
387 #endif
388 
389 	/* allocate file system specific data structure */
390 	fp = alloc(sizeof(struct file));
391 	bzero(fp, sizeof(struct file));
392 	f->f_fsdata = (void *)fp;
393 
394 	/* allocate space and read super block */
395 	fs = alloc(SBSIZE);
396 	fp->f_fs = fs;
397 	rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
398 		SBLOCK, SBSIZE, (char *)fs, &buf_size);
399 	if (rc)
400 		goto out;
401 
402 	if (buf_size != SBSIZE || fs->fs_magic != FS_MAGIC ||
403 	    fs->fs_bsize > MAXBSIZE || fs->fs_bsize < sizeof(struct fs)) {
404 		rc = EINVAL;
405 		goto out;
406 	}
407 #ifdef COMPAT_UFS
408 	ffs_oldfscompat(fs);
409 #endif
410 
411 	/*
412 	 * Calculate indirect block levels.
413 	 */
414 	{
415 		register int mult;
416 		register int level;
417 
418 		mult = 1;
419 		for (level = 0; level < NIADDR; level++) {
420 			mult *= NINDIR(fs);
421 			fp->f_nindir[level] = mult;
422 		}
423 	}
424 
425 	inumber = ROOTINO;
426 	if ((rc = read_inode(inumber, f)) != 0)
427 		goto out;
428 
429 	cp = path;
430 	while (*cp) {
431 
432 		/*
433 		 * Remove extra separators
434 		 */
435 		while (*cp == '/')
436 			cp++;
437 		if (*cp == '\0')
438 			break;
439 
440 		/*
441 		 * Check that current node is a directory.
442 		 */
443 		if ((fp->f_di.di_mode & IFMT) != IFDIR) {
444 			rc = ENOTDIR;
445 			goto out;
446 		}
447 
448 		/*
449 		 * Get next component of path name.
450 		 */
451 		{
452 			register int len = 0;
453 
454 			ncp = cp;
455 			while ((c = *cp) != '\0' && c != '/') {
456 				if (++len > MAXNAMLEN) {
457 					rc = ENOENT;
458 					goto out;
459 				}
460 				cp++;
461 			}
462 			*cp = '\0';
463 		}
464 
465 		/*
466 		 * Look up component in current directory.
467 		 * Save directory inumber in case we find a
468 		 * symbolic link.
469 		 */
470 		parent_inumber = inumber;
471 		rc = search_directory(ncp, f, &inumber);
472 		*cp = c;
473 		if (rc)
474 			goto out;
475 
476 		/*
477 		 * Open next component.
478 		 */
479 		if ((rc = read_inode(inumber, f)) != 0)
480 			goto out;
481 
482 #if 0
483 		/*
484 		 * Check for symbolic link.
485 		 */
486 		if ((fp->i_mode & IFMT) == IFLNK) {
487 			int link_len = fp->f_di.di_size;
488 			int len;
489 
490 			len = strlen(cp) + 1;
491 
492 			if (fp->f_di.di_size >= MAXPATHLEN - 1 ||
493 			    ++nlinks > MAXSYMLINKS) {
494 				rc = ENOENT;
495 				goto out;
496 			}
497 
498 			strcpy(&namebuf[link_len], cp);
499 
500 			if ((fp->i_flags & IC_FASTLINK) != 0) {
501 				bcopy(fp->i_symlink, namebuf, (unsigned) link_len);
502 			} else {
503 				/*
504 				 * Read file for symbolic link
505 				 */
506 				char *buf;
507 				u_int buf_size;
508 				daddr_t	disk_block;
509 				register struct fs *fs = fp->f_fs;
510 
511 				(void) block_map(f, (daddr_t)0, &disk_block);
512 				rc = device_read(&fp->f_dev,
513 						 fsbtodb(fs, disk_block),
514 						 blksize(fs, fp, 0),
515 						 &buf, &buf_size);
516 				if (rc)
517 					goto out;
518 
519 				bcopy((char *)buf, namebuf, (unsigned)link_len);
520 				free(buf, buf_size);
521 			}
522 
523 			/*
524 			 * If relative pathname, restart at parent directory.
525 			 * If absolute pathname, restart at root.
526 			 */
527 			cp = namebuf;
528 			if (*cp != '/')
529 				inumber = parent_inumber;
530 			else
531 				inumber = (ino_t)ROOTINO;
532 
533 			if ((rc = read_inode(inumber, fp)) != 0)
534 				goto out;
535 		}
536 #endif
537 	}
538 
539 	/*
540 	 * Found terminal component.
541 	 */
542 	rc = 0;
543 out:
544 	if (rc)
545 		free(fp, sizeof(struct file));
546 	return (rc);
547 }
548 
549 int
550 ufs_close(f)
551 	struct open_file *f;
552 {
553 	register struct file *fp = (struct file *)f->f_fsdata;
554 	int level;
555 
556 	f->f_fsdata = (void *)0;
557 	if (fp == (struct file *)0)
558 		return (0);
559 
560 	for (level = 0; level < NIADDR; level++) {
561 		if (fp->f_blk[level])
562 			free(fp->f_blk[level], fp->f_fs->fs_bsize);
563 	}
564 	if (fp->f_buf)
565 		free(fp->f_buf, fp->f_fs->fs_bsize);
566 	free(fp->f_fs, SBSIZE);
567 	free(fp, sizeof(struct file));
568 	return (0);
569 }
570 
571 /*
572  * Copy a portion of a file into kernel memory.
573  * Cross block boundaries when necessary.
574  */
575 int
576 ufs_read(f, start, size, resid)
577 	struct open_file *f;
578 	char *start;
579 	u_int size;
580 	u_int *resid;	/* out */
581 {
582 	register struct file *fp = (struct file *)f->f_fsdata;
583 	register u_int csize;
584 	char *buf;
585 	u_int buf_size;
586 	int rc = 0;
587 
588 	while (size != 0) {
589 		if (fp->f_seekp >= fp->f_di.di_size)
590 			break;
591 
592 		rc = buf_read_file(f, &buf, &buf_size);
593 		if (rc)
594 			break;
595 
596 		csize = size;
597 		if (csize > buf_size)
598 			csize = buf_size;
599 
600 		bcopy(buf, start, csize);
601 
602 		fp->f_seekp += csize;
603 		start += csize;
604 		size -= csize;
605 	}
606 	if (resid)
607 		*resid = size;
608 	return (rc);
609 }
610 
611 /*
612  * Not implemented.
613  */
614 int
615 ufs_write(f, start, size, resid)
616 	struct open_file *f;
617 	char *start;
618 	u_int size;
619 	u_int *resid;	/* out */
620 {
621 
622 	return (EROFS);
623 }
624 
625 off_t
626 ufs_seek(f, offset, where)
627 	struct open_file *f;
628 	off_t offset;
629 	int where;
630 {
631 	register struct file *fp = (struct file *)f->f_fsdata;
632 
633 	switch (where) {
634 	case SEEK_SET:
635 		fp->f_seekp = offset;
636 		break;
637 	case SEEK_CUR:
638 		fp->f_seekp += offset;
639 		break;
640 	case SEEK_END:
641 		fp->f_seekp = fp->f_di.di_size - offset;
642 		break;
643 	default:
644 		return (-1);
645 	}
646 	return (fp->f_seekp);
647 }
648 
649 int
650 ufs_stat(f, sb)
651 	struct open_file *f;
652 	struct stat *sb;
653 {
654 	register struct file *fp = (struct file *)f->f_fsdata;
655 
656 	/* only important stuff */
657 	sb->st_mode = fp->f_di.di_mode;
658 	sb->st_uid = fp->f_di.di_uid;
659 	sb->st_gid = fp->f_di.di_gid;
660 	sb->st_size = fp->f_di.di_size;
661 	return (0);
662 }
663 
664 #ifdef COMPAT_UFS
665 /*
666  * Sanity checks for old file systems.
667  *
668  * XXX - goes away some day.
669  */
670 void
671 ffs_oldfscompat(fs)
672 	struct fs *fs;
673 {
674 	int i;
675 
676 	fs->fs_npsect = max(fs->fs_npsect, fs->fs_nsect);	/* XXX */
677 	fs->fs_interleave = max(fs->fs_interleave, 1);		/* XXX */
678 	if (fs->fs_postblformat == FS_42POSTBLFMT)		/* XXX */
679 		fs->fs_nrpos = 8;				/* XXX */
680 	if (fs->fs_inodefmt < FS_44INODEFMT) {			/* XXX */
681 		quad_t sizepb = fs->fs_bsize;			/* XXX */
682 								/* XXX */
683 		fs->fs_maxfilesize = fs->fs_bsize * NDADDR - 1;	/* XXX */
684 		for (i = 0; i < NIADDR; i++) {			/* XXX */
685 			sizepb *= NINDIR(fs);			/* XXX */
686 			fs->fs_maxfilesize += sizepb;		/* XXX */
687 		}						/* XXX */
688 		fs->fs_qbmask = ~fs->fs_bmask;			/* XXX */
689 		fs->fs_qfmask = ~fs->fs_fmask;			/* XXX */
690 	}							/* XXX */
691 }
692 #endif
693