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