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