xref: /original-bsd/sys/ufs/lfs/lfs_balloc.c (revision 80a2c5b1)
1 /*	lfs_balloc.c	5.4	83/03/15	*/
2 
3 #include "../h/param.h"
4 #include "../h/systm.h"
5 #include "../h/conf.h"
6 #include "../h/inode.h"
7 #include "../h/dir.h"
8 #include "../h/user.h"
9 #include "../h/buf.h"
10 #include "../h/proc.h"
11 #include "../h/fs.h"
12 
13 /*
14  * Bmap defines the structure of file system storage
15  * by returning the physical block number on a device given the
16  * inode and the logical block number in a file.
17  * When convenient, it also leaves the physical
18  * block number of the next block of the file in rablock
19  * for use in read-ahead.
20  */
21 /*VARARGS3*/
22 daddr_t
23 bmap(ip, bn, rwflg, size)
24 	register struct inode *ip;
25 	daddr_t bn;
26 	int rwflg;
27 	int size;	/* supplied only when rwflg == B_WRITE */
28 {
29 	register int i;
30 	int osize, nsize;
31 	struct buf *bp, *nbp;
32 	struct fs *fs;
33 	int j, sh;
34 	daddr_t nb, lbn, *bap, pref, blkpref();
35 
36 	if (bn < 0) {
37 		u.u_error = EFBIG;
38 		return ((daddr_t)0);
39 	}
40 	fs = ip->i_fs;
41 	rablock = 0;
42 	rasize = 0;		/* conservative */
43 
44 	/*
45 	 * If the next write will extend the file into a new block,
46 	 * and the file is currently composed of a fragment
47 	 * this fragment has to be extended to be a full block.
48 	 */
49 	nb = lblkno(fs, ip->i_size);
50 	if (rwflg == B_WRITE && nb < NDADDR && nb < bn) {
51 		osize = blksize(fs, ip, nb);
52 		if (osize < fs->fs_bsize && osize > 0) {
53 			bp = realloccg(ip, ip->i_db[nb],
54 				blkpref(ip, nb, (int)nb, &ip->i_db[0]),
55 				osize, (int)fs->fs_bsize);
56 			if (bp == NULL)
57 				return ((daddr_t)-1);
58 			ip->i_size = (nb + 1) * fs->fs_bsize;
59 			ip->i_db[nb] = dbtofsb(fs, bp->b_blkno);
60 			ip->i_flag |= IUPD|ICHG;
61 			bdwrite(bp);
62 		}
63 	}
64 	/*
65 	 * The first NDADDR blocks are direct blocks
66 	 */
67 	if (bn < NDADDR) {
68 		nb = ip->i_db[bn];
69 		if (rwflg == B_READ) {
70 			if (nb == 0)
71 				return ((daddr_t)-1);
72 			goto gotit;
73 		}
74 		if (nb == 0 || ip->i_size < (bn + 1) * fs->fs_bsize) {
75 			if (nb != 0) {
76 				/* consider need to reallocate a frag */
77 				osize = fragroundup(fs, blkoff(fs, ip->i_size));
78 				nsize = fragroundup(fs, size);
79 				if (nsize <= osize)
80 					goto gotit;
81 				bp = realloccg(ip, nb,
82 					blkpref(ip, bn, (int)bn, &ip->i_db[0]),
83 					osize, nsize);
84 			} else {
85 				if (ip->i_size < (bn + 1) * fs->fs_bsize)
86 					nsize = fragroundup(fs, size);
87 				else
88 					nsize = fs->fs_bsize;
89 				bp = alloc(ip,
90 					blkpref(ip, bn, (int)bn, &ip->i_db[0]),
91 					nsize);
92 			}
93 			if (bp == NULL)
94 				return ((daddr_t)-1);
95 			nb = dbtofsb(fs, bp->b_blkno);
96 			if ((ip->i_mode&IFMT) == IFDIR)
97 				/*
98 				 * Write directory blocks synchronously
99 				 * so they never appear with garbage in
100 				 * them on the disk.
101 				 */
102 				bwrite(bp);
103 			else
104 				bdwrite(bp);
105 			ip->i_db[bn] = nb;
106 			ip->i_flag |= IUPD|ICHG;
107 		}
108 gotit:
109 		if (bn < NDADDR - 1) {
110 			rablock = fsbtodb(fs, ip->i_db[bn + 1]);
111 			rasize = blksize(fs, ip, bn + 1);
112 		}
113 		return (nb);
114 	}
115 
116 	/*
117 	 * Determine how many levels of indirection.
118 	 */
119 	pref = 0;
120 	sh = 1;
121 	lbn = bn;
122 	bn -= NDADDR;
123 	for (j = NIADDR; j>0; j--) {
124 		sh *= NINDIR(fs);
125 		if (bn < sh)
126 			break;
127 		bn -= sh;
128 	}
129 	if (j == 0) {
130 		u.u_error = EFBIG;
131 		return ((daddr_t)0);
132 	}
133 
134 	/*
135 	 * fetch the first indirect block
136 	 */
137 	nb = ip->i_ib[NIADDR - j];
138 	if (nb == 0) {
139 		if (rwflg == B_READ)
140 			return ((daddr_t)-1);
141 		pref = blkpref(ip, lbn, 0, (daddr_t *)0);
142 	        bp = alloc(ip, pref, (int)fs->fs_bsize);
143 		if (bp == NULL)
144 			return ((daddr_t)-1);
145 		nb = dbtofsb(fs, bp->b_blkno);
146 		/*
147 		 * Write synchronously so that indirect blocks
148 		 * never point at garbage.
149 		 */
150 		bwrite(bp);
151 		ip->i_ib[NIADDR - j] = nb;
152 		ip->i_flag |= IUPD|ICHG;
153 	}
154 
155 	/*
156 	 * fetch through the indirect blocks
157 	 */
158 	for (; j <= NIADDR; j++) {
159 		bp = bread(ip->i_dev, fsbtodb(fs, nb), (int)fs->fs_bsize);
160 		if (bp->b_flags & B_ERROR) {
161 			brelse(bp);
162 			return ((daddr_t)0);
163 		}
164 		bap = bp->b_un.b_daddr;
165 		sh /= NINDIR(fs);
166 		i = (bn / sh) % NINDIR(fs);
167 		nb = bap[i];
168 		if (nb == 0) {
169 			if (rwflg==B_READ) {
170 				brelse(bp);
171 				return ((daddr_t)-1);
172 			}
173 			if (pref == 0)
174 				if (j < NIADDR)
175 					pref = blkpref(ip, lbn, 0,
176 						(daddr_t *)0);
177 				else
178 					pref = blkpref(ip, lbn, i, &bap[0]);
179 		        nbp = alloc(ip, pref, (int)fs->fs_bsize);
180 			if (nbp == NULL) {
181 				brelse(bp);
182 				return ((daddr_t)-1);
183 			}
184 			nb = dbtofsb(fs, nbp->b_blkno);
185 			if (j < NIADDR || (ip->i_mode&IFMT) == IFDIR)
186 				/*
187 				 * Write synchronously so indirect blocks
188 				 * never point at garbage and blocks
189 				 * in directories never contain garbage.
190 				 */
191 				bwrite(nbp);
192 			else
193 				bdwrite(nbp);
194 			bap[i] = nb;
195 			bdwrite(bp);
196 		} else
197 			brelse(bp);
198 	}
199 
200 	/*
201 	 * calculate read-ahead.
202 	 */
203 	if (i < NINDIR(fs) - 1) {
204 		rablock = fsbtodb(fs, bap[i+1]);
205 		rasize = fs->fs_bsize;
206 	}
207 	return (nb);
208 }
209