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