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