1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1980, 1986, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#if 0
33#ifndef lint
34static const char sccsid[] = "@(#)inode.c	8.8 (Berkeley) 4/28/95";
35#endif /* not lint */
36#endif
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD$");
39
40#include <sys/param.h>
41#include <sys/stdint.h>
42#include <sys/sysctl.h>
43
44#include <ufs/ufs/dinode.h>
45#include <ufs/ufs/dir.h>
46#include <ufs/ffs/fs.h>
47
48#include <err.h>
49#include <pwd.h>
50#include <string.h>
51#include <time.h>
52
53#include "fsck.h"
54
55static ino_t startinum;
56
57static int iblock(struct inodesc *, off_t isize, int type);
58
59int
60ckinode(union dinode *dp, struct inodesc *idesc)
61{
62	off_t remsize, sizepb;
63	int i, offset, ret;
64	union dinode dino;
65	ufs2_daddr_t ndb;
66	mode_t mode;
67	char pathbuf[MAXPATHLEN + 1];
68
69	if (idesc->id_fix != IGNORE)
70		idesc->id_fix = DONTKNOW;
71	idesc->id_lbn = -1;
72	idesc->id_lballoc = -1;
73	idesc->id_level = 0;
74	idesc->id_entryno = 0;
75	idesc->id_filesize = DIP(dp, di_size);
76	mode = DIP(dp, di_mode) & IFMT;
77	if (mode == IFBLK || mode == IFCHR || (mode == IFLNK &&
78	    DIP(dp, di_size) < (unsigned)sblock.fs_maxsymlinklen))
79		return (KEEPON);
80	if (sblock.fs_magic == FS_UFS1_MAGIC)
81		dino.dp1 = dp->dp1;
82	else
83		dino.dp2 = dp->dp2;
84	ndb = howmany(DIP(&dino, di_size), sblock.fs_bsize);
85	for (i = 0; i < UFS_NDADDR; i++) {
86		idesc->id_lbn++;
87		if (--ndb == 0 &&
88		    (offset = blkoff(&sblock, DIP(&dino, di_size))) != 0)
89			idesc->id_numfrags =
90				numfrags(&sblock, fragroundup(&sblock, offset));
91		else
92			idesc->id_numfrags = sblock.fs_frag;
93		if (DIP(&dino, di_db[i]) == 0) {
94			if (idesc->id_type == DATA && ndb >= 0) {
95				/* An empty block in a directory XXX */
96				getpathname(pathbuf, idesc->id_number,
97						idesc->id_number);
98				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
99					pathbuf);
100				if (reply("ADJUST LENGTH") == 1) {
101					dp = ginode(idesc->id_number);
102					DIP_SET(dp, di_size,
103					    i * sblock.fs_bsize);
104					printf(
105					    "YOU MUST RERUN FSCK AFTERWARDS\n");
106					rerun = 1;
107					inodirty(dp);
108
109				}
110			}
111			continue;
112		}
113		idesc->id_blkno = DIP(&dino, di_db[i]);
114		if (idesc->id_type != DATA)
115			ret = (*idesc->id_func)(idesc);
116		else
117			ret = dirscan(idesc);
118		if (ret & STOP)
119			return (ret);
120	}
121	idesc->id_numfrags = sblock.fs_frag;
122	remsize = DIP(&dino, di_size) - sblock.fs_bsize * UFS_NDADDR;
123	sizepb = sblock.fs_bsize;
124	for (i = 0; i < UFS_NIADDR; i++) {
125		sizepb *= NINDIR(&sblock);
126		idesc->id_level = i + 1;
127		if (DIP(&dino, di_ib[i])) {
128			idesc->id_blkno = DIP(&dino, di_ib[i]);
129			ret = iblock(idesc, remsize, BT_LEVEL1 + i);
130			if (ret & STOP)
131				return (ret);
132		} else if (remsize > 0) {
133			idesc->id_lbn += sizepb / sblock.fs_bsize;
134			if (idesc->id_type == DATA) {
135				/* An empty block in a directory XXX */
136				getpathname(pathbuf, idesc->id_number,
137						idesc->id_number);
138				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
139					pathbuf);
140				if (reply("ADJUST LENGTH") == 1) {
141					dp = ginode(idesc->id_number);
142					DIP_SET(dp, di_size,
143					    DIP(dp, di_size) - remsize);
144					remsize = 0;
145					printf(
146					    "YOU MUST RERUN FSCK AFTERWARDS\n");
147					rerun = 1;
148					inodirty(dp);
149					break;
150				}
151			}
152		}
153		remsize -= sizepb;
154	}
155	return (KEEPON);
156}
157
158static int
159iblock(struct inodesc *idesc, off_t isize, int type)
160{
161	struct bufarea *bp;
162	int i, n, (*func)(struct inodesc *), nif;
163	off_t sizepb;
164	char buf[BUFSIZ];
165	char pathbuf[MAXPATHLEN + 1];
166	union dinode *dp;
167
168	if (idesc->id_type != DATA) {
169		func = idesc->id_func;
170		if (((n = (*func)(idesc)) & KEEPON) == 0)
171			return (n);
172	} else
173		func = dirscan;
174	if (chkrange(idesc->id_blkno, idesc->id_numfrags))
175		return (SKIP);
176	bp = getdatablk(idesc->id_blkno, sblock.fs_bsize, type);
177	idesc->id_level--;
178	for (sizepb = sblock.fs_bsize, i = 0; i < idesc->id_level; i++)
179		sizepb *= NINDIR(&sblock);
180	if (howmany(isize, sizepb) > NINDIR(&sblock))
181		nif = NINDIR(&sblock);
182	else
183		nif = howmany(isize, sizepb);
184	if (idesc->id_func == pass1check && nif < NINDIR(&sblock)) {
185		for (i = nif; i < NINDIR(&sblock); i++) {
186			if (IBLK(bp, i) == 0)
187				continue;
188			(void)sprintf(buf, "PARTIALLY TRUNCATED INODE I=%lu",
189			    (u_long)idesc->id_number);
190			if (preen) {
191				pfatal("%s", buf);
192			} else if (dofix(idesc, buf)) {
193				IBLK_SET(bp, i, 0);
194				dirty(bp);
195			}
196		}
197		flush(fswritefd, bp);
198	}
199	for (i = 0; i < nif; i++) {
200		if (IBLK(bp, i)) {
201			idesc->id_blkno = IBLK(bp, i);
202			if (idesc->id_level == 0) {
203				idesc->id_lbn++;
204				n = (*func)(idesc);
205			} else {
206				n = iblock(idesc, isize, type);
207				idesc->id_level++;
208			}
209			if (n & STOP) {
210				bp->b_flags &= ~B_INUSE;
211				return (n);
212			}
213		} else {
214			idesc->id_lbn += sizepb / sblock.fs_bsize;
215			if (idesc->id_type == DATA && isize > 0) {
216				/* An empty block in a directory XXX */
217				getpathname(pathbuf, idesc->id_number,
218						idesc->id_number);
219				pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS",
220					pathbuf);
221				if (reply("ADJUST LENGTH") == 1) {
222					dp = ginode(idesc->id_number);
223					DIP_SET(dp, di_size,
224					    DIP(dp, di_size) - isize);
225					isize = 0;
226					printf(
227					    "YOU MUST RERUN FSCK AFTERWARDS\n");
228					rerun = 1;
229					inodirty(dp);
230					bp->b_flags &= ~B_INUSE;
231					return(STOP);
232				}
233			}
234		}
235		isize -= sizepb;
236	}
237	bp->b_flags &= ~B_INUSE;
238	return (KEEPON);
239}
240
241/*
242 * Check that a block in a legal block number.
243 * Return 0 if in range, 1 if out of range.
244 */
245int
246chkrange(ufs2_daddr_t blk, int cnt)
247{
248	int c;
249
250	if (cnt <= 0 || blk <= 0 || blk > maxfsblock ||
251	    cnt - 1 > maxfsblock - blk)
252		return (1);
253	if (cnt > sblock.fs_frag ||
254	    fragnum(&sblock, blk) + cnt > sblock.fs_frag) {
255		if (debug)
256			printf("bad size: blk %ld, offset %i, size %d\n",
257			    (long)blk, (int)fragnum(&sblock, blk), cnt);
258		return (1);
259	}
260	c = dtog(&sblock, blk);
261	if (blk < cgdmin(&sblock, c)) {
262		if ((blk + cnt) > cgsblock(&sblock, c)) {
263			if (debug) {
264				printf("blk %ld < cgdmin %ld;",
265				    (long)blk, (long)cgdmin(&sblock, c));
266				printf(" blk + cnt %ld > cgsbase %ld\n",
267				    (long)(blk + cnt),
268				    (long)cgsblock(&sblock, c));
269			}
270			return (1);
271		}
272	} else {
273		if ((blk + cnt) > cgbase(&sblock, c+1)) {
274			if (debug)  {
275				printf("blk %ld >= cgdmin %ld;",
276				    (long)blk, (long)cgdmin(&sblock, c));
277				printf(" blk + cnt %ld > sblock.fs_fpg %ld\n",
278				    (long)(blk + cnt), (long)sblock.fs_fpg);
279			}
280			return (1);
281		}
282	}
283	return (0);
284}
285
286/*
287 * General purpose interface for reading inodes.
288 */
289union dinode *
290ginode(ino_t inumber)
291{
292	ufs2_daddr_t iblk;
293
294	if (inumber < UFS_ROOTINO || inumber > maxino)
295		errx(EEXIT, "bad inode number %ju to ginode",
296		    (uintmax_t)inumber);
297	if (startinum == 0 ||
298	    inumber < startinum || inumber >= startinum + INOPB(&sblock)) {
299		iblk = ino_to_fsba(&sblock, inumber);
300		if (pbp != NULL)
301			pbp->b_flags &= ~B_INUSE;
302		pbp = getdatablk(iblk, sblock.fs_bsize, BT_INODES);
303		startinum = rounddown(inumber, INOPB(&sblock));
304	}
305	if (sblock.fs_magic == FS_UFS1_MAGIC)
306		return ((union dinode *)
307		    &pbp->b_un.b_dinode1[inumber % INOPB(&sblock)]);
308	return ((union dinode *)&pbp->b_un.b_dinode2[inumber % INOPB(&sblock)]);
309}
310
311/*
312 * Special purpose version of ginode used to optimize first pass
313 * over all the inodes in numerical order.
314 */
315static ino_t nextino, lastinum, lastvalidinum;
316static long readcount, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
317static struct bufarea inobuf;
318
319union dinode *
320getnextinode(ino_t inumber, int rebuildcg)
321{
322	int j;
323	long size;
324	mode_t mode;
325	ufs2_daddr_t ndb, blk;
326	union dinode *dp;
327	static caddr_t nextinop;
328
329	if (inumber != nextino++ || inumber > lastvalidinum)
330		errx(EEXIT, "bad inode number %ju to nextinode",
331		    (uintmax_t)inumber);
332	if (inumber >= lastinum) {
333		readcount++;
334		blk = ino_to_fsba(&sblock, lastinum);
335		if (readcount % readpercg == 0) {
336			size = partialsize;
337			lastinum += partialcnt;
338		} else {
339			size = inobufsize;
340			lastinum += fullcnt;
341		}
342		/*
343		 * If getblk encounters an error, it will already have zeroed
344		 * out the buffer, so we do not need to do so here.
345		 */
346		getblk(&inobuf, blk, size);
347		nextinop = inobuf.b_un.b_buf;
348	}
349	dp = (union dinode *)nextinop;
350	if (rebuildcg && nextinop == inobuf.b_un.b_buf) {
351		/*
352		 * Try to determine if we have reached the end of the
353		 * allocated inodes.
354		 */
355		mode = DIP(dp, di_mode) & IFMT;
356		if (mode == 0) {
357			if (memcmp(dp->dp2.di_db, ufs2_zino.di_db,
358				UFS_NDADDR * sizeof(ufs2_daddr_t)) ||
359			      memcmp(dp->dp2.di_ib, ufs2_zino.di_ib,
360				UFS_NIADDR * sizeof(ufs2_daddr_t)) ||
361			      dp->dp2.di_mode || dp->dp2.di_size)
362				return (NULL);
363			goto inodegood;
364		}
365		if (!ftypeok(dp))
366			return (NULL);
367		ndb = howmany(DIP(dp, di_size), sblock.fs_bsize);
368		if (ndb < 0)
369			return (NULL);
370		if (mode == IFBLK || mode == IFCHR)
371			ndb++;
372		if (mode == IFLNK) {
373			/*
374			 * Fake ndb value so direct/indirect block checks below
375			 * will detect any garbage after symlink string.
376			 */
377			if (DIP(dp, di_size) < (off_t)sblock.fs_maxsymlinklen) {
378				ndb = howmany(DIP(dp, di_size),
379				    sizeof(ufs2_daddr_t));
380				if (ndb > UFS_NDADDR) {
381					j = ndb - UFS_NDADDR;
382					for (ndb = 1; j > 1; j--)
383						ndb *= NINDIR(&sblock);
384					ndb += UFS_NDADDR;
385				}
386			}
387		}
388		for (j = ndb; ndb < UFS_NDADDR && j < UFS_NDADDR; j++)
389			if (DIP(dp, di_db[j]) != 0)
390				return (NULL);
391		for (j = 0, ndb -= UFS_NDADDR; ndb > 0; j++)
392			ndb /= NINDIR(&sblock);
393		for (; j < UFS_NIADDR; j++)
394			if (DIP(dp, di_ib[j]) != 0)
395				return (NULL);
396	}
397inodegood:
398	if (sblock.fs_magic == FS_UFS1_MAGIC)
399		nextinop += sizeof(struct ufs1_dinode);
400	else
401		nextinop += sizeof(struct ufs2_dinode);
402	return (dp);
403}
404
405void
406setinodebuf(ino_t inum)
407{
408
409	if (inum % sblock.fs_ipg != 0)
410		errx(EEXIT, "bad inode number %ju to setinodebuf",
411		    (uintmax_t)inum);
412	lastvalidinum = inum + sblock.fs_ipg - 1;
413	startinum = 0;
414	nextino = inum;
415	lastinum = inum;
416	readcount = 0;
417	if (inobuf.b_un.b_buf != NULL)
418		return;
419	inobufsize = blkroundup(&sblock, INOBUFSIZE);
420	fullcnt = inobufsize / ((sblock.fs_magic == FS_UFS1_MAGIC) ?
421	    sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode));
422	readpercg = sblock.fs_ipg / fullcnt;
423	partialcnt = sblock.fs_ipg % fullcnt;
424	partialsize = partialcnt * ((sblock.fs_magic == FS_UFS1_MAGIC) ?
425	    sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode));
426	if (partialcnt != 0) {
427		readpercg++;
428	} else {
429		partialcnt = fullcnt;
430		partialsize = inobufsize;
431	}
432	initbarea(&inobuf, BT_INODES);
433	if ((inobuf.b_un.b_buf = Malloc((unsigned)inobufsize)) == NULL)
434		errx(EEXIT, "cannot allocate space for inode buffer");
435}
436
437void
438freeinodebuf(void)
439{
440
441	if (inobuf.b_un.b_buf != NULL)
442		free((char *)inobuf.b_un.b_buf);
443	inobuf.b_un.b_buf = NULL;
444}
445
446/*
447 * Routines to maintain information about directory inodes.
448 * This is built during the first pass and used during the
449 * second and third passes.
450 *
451 * Enter inodes into the cache.
452 */
453void
454cacheino(union dinode *dp, ino_t inumber)
455{
456	struct inoinfo *inp, **inpp;
457	int i, blks;
458
459	if (howmany(DIP(dp, di_size), sblock.fs_bsize) > UFS_NDADDR)
460		blks = UFS_NDADDR + UFS_NIADDR;
461	else if (DIP(dp, di_size) > 0)
462		blks = howmany(DIP(dp, di_size), sblock.fs_bsize);
463	else
464		blks = 1;
465	inp = (struct inoinfo *)
466		Malloc(sizeof(*inp) + (blks - 1) * sizeof(ufs2_daddr_t));
467	if (inp == NULL)
468		errx(EEXIT, "cannot increase directory list");
469	inpp = &inphead[inumber % dirhash];
470	inp->i_nexthash = *inpp;
471	*inpp = inp;
472	inp->i_parent = inumber == UFS_ROOTINO ? UFS_ROOTINO : (ino_t)0;
473	inp->i_dotdot = (ino_t)0;
474	inp->i_number = inumber;
475	inp->i_isize = DIP(dp, di_size);
476	inp->i_numblks = blks;
477	for (i = 0; i < MIN(blks, UFS_NDADDR); i++)
478		inp->i_blks[i] = DIP(dp, di_db[i]);
479	if (blks > UFS_NDADDR)
480		for (i = 0; i < UFS_NIADDR; i++)
481			inp->i_blks[UFS_NDADDR + i] = DIP(dp, di_ib[i]);
482	if (inplast == listmax) {
483		listmax += 100;
484		inpsort = (struct inoinfo **)reallocarray((char *)inpsort,
485		    listmax, sizeof(struct inoinfo *));
486		if (inpsort == NULL)
487			errx(EEXIT, "cannot increase directory list");
488	}
489	inpsort[inplast++] = inp;
490}
491
492/*
493 * Look up an inode cache structure.
494 */
495struct inoinfo *
496getinoinfo(ino_t inumber)
497{
498	struct inoinfo *inp;
499
500	for (inp = inphead[inumber % dirhash]; inp; inp = inp->i_nexthash) {
501		if (inp->i_number != inumber)
502			continue;
503		return (inp);
504	}
505	errx(EEXIT, "cannot find inode %ju", (uintmax_t)inumber);
506	return ((struct inoinfo *)0);
507}
508
509/*
510 * Clean up all the inode cache structure.
511 */
512void
513inocleanup(void)
514{
515	struct inoinfo **inpp;
516
517	if (inphead == NULL)
518		return;
519	for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--)
520		free((char *)(*inpp));
521	free((char *)inphead);
522	free((char *)inpsort);
523	inphead = inpsort = NULL;
524}
525
526void
527inodirty(union dinode *dp)
528{
529
530	dirty(pbp);
531}
532
533void
534clri(struct inodesc *idesc, const char *type, int flag)
535{
536	union dinode *dp;
537
538	dp = ginode(idesc->id_number);
539	if (flag == 1) {
540		pwarn("%s %s", type,
541		    (DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE");
542		pinode(idesc->id_number);
543	}
544	if (preen || reply("CLEAR") == 1) {
545		if (preen)
546			printf(" (CLEARED)\n");
547		n_files--;
548		if (bkgrdflag == 0) {
549			(void)ckinode(dp, idesc);
550			inoinfo(idesc->id_number)->ino_state = USTATE;
551			clearinode(dp);
552			inodirty(dp);
553		} else {
554			cmd.value = idesc->id_number;
555			cmd.size = -DIP(dp, di_nlink);
556			if (debug)
557				printf("adjrefcnt ino %ld amt %lld\n",
558				    (long)cmd.value, (long long)cmd.size);
559			if (sysctl(adjrefcnt, MIBSIZE, 0, 0,
560			    &cmd, sizeof cmd) == -1)
561				rwerror("ADJUST INODE", cmd.value);
562		}
563	}
564}
565
566int
567findname(struct inodesc *idesc)
568{
569	struct direct *dirp = idesc->id_dirp;
570
571	if (dirp->d_ino != idesc->id_parent || idesc->id_entryno < 2) {
572		idesc->id_entryno++;
573		return (KEEPON);
574	}
575	memmove(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1);
576	return (STOP|FOUND);
577}
578
579int
580findino(struct inodesc *idesc)
581{
582	struct direct *dirp = idesc->id_dirp;
583
584	if (dirp->d_ino == 0)
585		return (KEEPON);
586	if (strcmp(dirp->d_name, idesc->id_name) == 0 &&
587	    dirp->d_ino >= UFS_ROOTINO && dirp->d_ino <= maxino) {
588		idesc->id_parent = dirp->d_ino;
589		return (STOP|FOUND);
590	}
591	return (KEEPON);
592}
593
594int
595clearentry(struct inodesc *idesc)
596{
597	struct direct *dirp = idesc->id_dirp;
598
599	if (dirp->d_ino != idesc->id_parent || idesc->id_entryno < 2) {
600		idesc->id_entryno++;
601		return (KEEPON);
602	}
603	dirp->d_ino = 0;
604	return (STOP|FOUND|ALTERED);
605}
606
607void
608pinode(ino_t ino)
609{
610	union dinode *dp;
611	char *p;
612	struct passwd *pw;
613	time_t t;
614
615	printf(" I=%lu ", (u_long)ino);
616	if (ino < UFS_ROOTINO || ino > maxino)
617		return;
618	dp = ginode(ino);
619	printf(" OWNER=");
620	if ((pw = getpwuid((int)DIP(dp, di_uid))) != NULL)
621		printf("%s ", pw->pw_name);
622	else
623		printf("%u ", (unsigned)DIP(dp, di_uid));
624	printf("MODE=%o\n", DIP(dp, di_mode));
625	if (preen)
626		printf("%s: ", cdevname);
627	printf("SIZE=%ju ", (uintmax_t)DIP(dp, di_size));
628	t = DIP(dp, di_mtime);
629	p = ctime(&t);
630	printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]);
631}
632
633void
634blkerror(ino_t ino, const char *type, ufs2_daddr_t blk)
635{
636
637	pfatal("%jd %s I=%ju", (intmax_t)blk, type, (uintmax_t)ino);
638	printf("\n");
639	switch (inoinfo(ino)->ino_state) {
640
641	case FSTATE:
642	case FZLINK:
643		inoinfo(ino)->ino_state = FCLEAR;
644		return;
645
646	case DSTATE:
647	case DZLINK:
648		inoinfo(ino)->ino_state = DCLEAR;
649		return;
650
651	case FCLEAR:
652	case DCLEAR:
653		return;
654
655	default:
656		errx(EEXIT, "BAD STATE %d TO BLKERR", inoinfo(ino)->ino_state);
657		/* NOTREACHED */
658	}
659}
660
661/*
662 * allocate an unused inode
663 */
664ino_t
665allocino(ino_t request, int type)
666{
667	ino_t ino;
668	union dinode *dp;
669	struct bufarea *cgbp;
670	struct cg *cgp;
671	int cg, anyino;
672
673	anyino = 0;
674	if (request == 0) {
675		request = UFS_ROOTINO;
676		anyino = 1;
677	} else if (inoinfo(request)->ino_state != USTATE)
678		return (0);
679retry:
680	for (ino = request; ino < maxino; ino++)
681		if (inoinfo(ino)->ino_state == USTATE)
682			break;
683	if (ino >= maxino)
684		return (0);
685	cg = ino_to_cg(&sblock, ino);
686	cgbp = cglookup(cg);
687	cgp = cgbp->b_un.b_cg;
688	if (!check_cgmagic(cg, cgbp)) {
689		if (anyino == 0)
690			return (0);
691		request = (cg + 1) * sblock.fs_ipg;
692		goto retry;
693	}
694	setbit(cg_inosused(cgp), ino % sblock.fs_ipg);
695	cgp->cg_cs.cs_nifree--;
696	switch (type & IFMT) {
697	case IFDIR:
698		inoinfo(ino)->ino_state = DSTATE;
699		cgp->cg_cs.cs_ndir++;
700		break;
701	case IFREG:
702	case IFLNK:
703		inoinfo(ino)->ino_state = FSTATE;
704		break;
705	default:
706		return (0);
707	}
708	dirty(cgbp);
709	dp = ginode(ino);
710	DIP_SET(dp, di_db[0], allocblk((long)1));
711	if (DIP(dp, di_db[0]) == 0) {
712		inoinfo(ino)->ino_state = USTATE;
713		return (0);
714	}
715	DIP_SET(dp, di_mode, type);
716	DIP_SET(dp, di_flags, 0);
717	DIP_SET(dp, di_atime, time(NULL));
718	DIP_SET(dp, di_ctime, DIP(dp, di_atime));
719	DIP_SET(dp, di_mtime, DIP(dp, di_ctime));
720	DIP_SET(dp, di_mtimensec, 0);
721	DIP_SET(dp, di_ctimensec, 0);
722	DIP_SET(dp, di_atimensec, 0);
723	DIP_SET(dp, di_size, sblock.fs_fsize);
724	DIP_SET(dp, di_blocks, btodb(sblock.fs_fsize));
725	n_files++;
726	inodirty(dp);
727	inoinfo(ino)->ino_type = IFTODT(type);
728	return (ino);
729}
730
731/*
732 * deallocate an inode
733 */
734void
735freeino(ino_t ino)
736{
737	struct inodesc idesc;
738	union dinode *dp;
739
740	memset(&idesc, 0, sizeof(struct inodesc));
741	idesc.id_type = ADDR;
742	idesc.id_func = pass4check;
743	idesc.id_number = ino;
744	dp = ginode(ino);
745	(void)ckinode(dp, &idesc);
746	clearinode(dp);
747	inodirty(dp);
748	inoinfo(ino)->ino_state = USTATE;
749	n_files--;
750}
751