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