1 /*
2 * Copyright (c) 1982, 1986, 1989, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * %sccs.include.redist.c%
6 *
7 * @(#)ffs_balloc.c 8.8 (Berkeley) 06/16/95
8 */
9
10 #include <sys/param.h>
11 #include <sys/systm.h>
12 #include <sys/buf.h>
13 #include <sys/proc.h>
14 #include <sys/file.h>
15 #include <sys/vnode.h>
16
17 #include <vm/vm.h>
18
19 #include <ufs/ufs/quota.h>
20 #include <ufs/ufs/inode.h>
21 #include <ufs/ufs/ufs_extern.h>
22
23 #include <ufs/ffs/fs.h>
24 #include <ufs/ffs/ffs_extern.h>
25
26 /*
27 * Balloc defines the structure of file system storage
28 * by allocating the physical blocks on a device given
29 * the inode and the logical block number in a file.
30 */
ffs_balloc(ip,lbn,size,cred,bpp,flags)31 ffs_balloc(ip, lbn, size, cred, bpp, flags)
32 register struct inode *ip;
33 register ufs_daddr_t lbn;
34 int size;
35 struct ucred *cred;
36 struct buf **bpp;
37 int flags;
38 {
39 register struct fs *fs;
40 register ufs_daddr_t nb;
41 struct buf *bp, *nbp;
42 struct vnode *vp = ITOV(ip);
43 struct indir indirs[NIADDR + 2];
44 ufs_daddr_t newb, *bap, pref;
45 int deallocated, osize, nsize, num, i, error;
46 ufs_daddr_t *allocib, *blkp, *allocblk, allociblk[NIADDR + 1];
47
48 *bpp = NULL;
49 if (lbn < 0)
50 return (EFBIG);
51 fs = ip->i_fs;
52
53 /*
54 * If the next write will extend the file into a new block,
55 * and the file is currently composed of a fragment
56 * this fragment has to be extended to be a full block.
57 */
58 nb = lblkno(fs, ip->i_size);
59 if (nb < NDADDR && nb < lbn) {
60 osize = blksize(fs, ip, nb);
61 if (osize < fs->fs_bsize && osize > 0) {
62 error = ffs_realloccg(ip, nb,
63 ffs_blkpref(ip, nb, (int)nb, &ip->i_db[0]),
64 osize, (int)fs->fs_bsize, cred, &bp);
65 if (error)
66 return (error);
67 ip->i_size = (nb + 1) * fs->fs_bsize;
68 vnode_pager_setsize(vp, (u_long)ip->i_size);
69 ip->i_db[nb] = dbtofsb(fs, bp->b_blkno);
70 ip->i_flag |= IN_CHANGE | IN_UPDATE;
71 if (flags & B_SYNC)
72 bwrite(bp);
73 else
74 bawrite(bp);
75 }
76 }
77 /*
78 * The first NDADDR blocks are direct blocks
79 */
80 if (lbn < NDADDR) {
81 nb = ip->i_db[lbn];
82 if (nb != 0 && ip->i_size >= (lbn + 1) * fs->fs_bsize) {
83 error = bread(vp, lbn, fs->fs_bsize, NOCRED, &bp);
84 if (error) {
85 brelse(bp);
86 return (error);
87 }
88 *bpp = bp;
89 return (0);
90 }
91 if (nb != 0) {
92 /*
93 * Consider need to reallocate a fragment.
94 */
95 osize = fragroundup(fs, blkoff(fs, ip->i_size));
96 nsize = fragroundup(fs, size);
97 if (nsize <= osize) {
98 error = bread(vp, lbn, osize, NOCRED, &bp);
99 if (error) {
100 brelse(bp);
101 return (error);
102 }
103 } else {
104 error = ffs_realloccg(ip, lbn,
105 ffs_blkpref(ip, lbn, (int)lbn,
106 &ip->i_db[0]), osize, nsize, cred, &bp);
107 if (error)
108 return (error);
109 }
110 } else {
111 if (ip->i_size < (lbn + 1) * fs->fs_bsize)
112 nsize = fragroundup(fs, size);
113 else
114 nsize = fs->fs_bsize;
115 error = ffs_alloc(ip, lbn,
116 ffs_blkpref(ip, lbn, (int)lbn, &ip->i_db[0]),
117 nsize, cred, &newb);
118 if (error)
119 return (error);
120 bp = getblk(vp, lbn, nsize, 0, 0);
121 bp->b_blkno = fsbtodb(fs, newb);
122 if (flags & B_CLRBUF)
123 clrbuf(bp);
124 }
125 ip->i_db[lbn] = dbtofsb(fs, bp->b_blkno);
126 ip->i_flag |= IN_CHANGE | IN_UPDATE;
127 *bpp = bp;
128 return (0);
129 }
130 /*
131 * Determine the number of levels of indirection.
132 */
133 pref = 0;
134 if (error = ufs_getlbns(vp, lbn, indirs, &num))
135 return(error);
136 #ifdef DIAGNOSTIC
137 if (num < 1)
138 panic ("ffs_balloc: ufs_bmaparray returned indirect block\n");
139 #endif
140 /*
141 * Fetch the first indirect block allocating if necessary.
142 */
143 --num;
144 nb = ip->i_ib[indirs[0].in_off];
145 allocib = NULL;
146 allocblk = allociblk;
147 if (nb == 0) {
148 pref = ffs_blkpref(ip, lbn, 0, (ufs_daddr_t *)0);
149 if (error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize,
150 cred, &newb))
151 return (error);
152 nb = newb;
153 *allocblk++ = nb;
154 bp = getblk(vp, indirs[1].in_lbn, fs->fs_bsize, 0, 0);
155 bp->b_blkno = fsbtodb(fs, nb);
156 clrbuf(bp);
157 /*
158 * Write synchronously so that indirect blocks
159 * never point at garbage.
160 */
161 if (error = bwrite(bp))
162 goto fail;
163 allocib = &ip->i_ib[indirs[0].in_off];
164 *allocib = nb;
165 ip->i_flag |= IN_CHANGE | IN_UPDATE;
166 }
167 /*
168 * Fetch through the indirect blocks, allocating as necessary.
169 */
170 for (i = 1;;) {
171 error = bread(vp,
172 indirs[i].in_lbn, (int)fs->fs_bsize, NOCRED, &bp);
173 if (error) {
174 brelse(bp);
175 goto fail;
176 }
177 bap = (ufs_daddr_t *)bp->b_data;
178 nb = bap[indirs[i].in_off];
179 if (i == num)
180 break;
181 i += 1;
182 if (nb != 0) {
183 brelse(bp);
184 continue;
185 }
186 if (pref == 0)
187 pref = ffs_blkpref(ip, lbn, 0, (ufs_daddr_t *)0);
188 if (error =
189 ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, cred, &newb)) {
190 brelse(bp);
191 goto fail;
192 }
193 nb = newb;
194 *allocblk++ = nb;
195 nbp = getblk(vp, indirs[i].in_lbn, fs->fs_bsize, 0, 0);
196 nbp->b_blkno = fsbtodb(fs, nb);
197 clrbuf(nbp);
198 /*
199 * Write synchronously so that indirect blocks
200 * never point at garbage.
201 */
202 if (error = bwrite(nbp)) {
203 brelse(bp);
204 goto fail;
205 }
206 bap[indirs[i - 1].in_off] = nb;
207 /*
208 * If required, write synchronously, otherwise use
209 * delayed write.
210 */
211 if (flags & B_SYNC) {
212 bwrite(bp);
213 } else {
214 bdwrite(bp);
215 }
216 }
217 /*
218 * Get the data block, allocating if necessary.
219 */
220 if (nb == 0) {
221 pref = ffs_blkpref(ip, lbn, indirs[i].in_off, &bap[0]);
222 if (error = ffs_alloc(ip,
223 lbn, pref, (int)fs->fs_bsize, cred, &newb)) {
224 brelse(bp);
225 goto fail;
226 }
227 nb = newb;
228 *allocblk++ = nb;
229 nbp = getblk(vp, lbn, fs->fs_bsize, 0, 0);
230 nbp->b_blkno = fsbtodb(fs, nb);
231 if (flags & B_CLRBUF)
232 clrbuf(nbp);
233 bap[indirs[i].in_off] = nb;
234 /*
235 * If required, write synchronously, otherwise use
236 * delayed write.
237 */
238 if (flags & B_SYNC) {
239 bwrite(bp);
240 } else {
241 bdwrite(bp);
242 }
243 *bpp = nbp;
244 return (0);
245 }
246 brelse(bp);
247 if (flags & B_CLRBUF) {
248 error = bread(vp, lbn, (int)fs->fs_bsize, NOCRED, &nbp);
249 if (error) {
250 brelse(nbp);
251 goto fail;
252 }
253 } else {
254 nbp = getblk(vp, lbn, fs->fs_bsize, 0, 0);
255 nbp->b_blkno = fsbtodb(fs, nb);
256 }
257 *bpp = nbp;
258 return (0);
259 fail:
260 /*
261 * If we have failed part way through block allocation, we
262 * have to deallocate any indirect blocks that we have allocated.
263 */
264 for (deallocated = 0, blkp = allociblk; blkp < allocblk; blkp++) {
265 ffs_blkfree(ip, *blkp, fs->fs_bsize);
266 deallocated += fs->fs_bsize;
267 }
268 if (allocib != NULL)
269 *allocib = 0;
270 if (deallocated) {
271 #ifdef QUOTA
272 /*
273 * Restore user's disk quota because allocation failed.
274 */
275 (void) chkdq(ip, (long)-btodb(deallocated), cred, FORCE);
276 #endif
277 ip->i_blocks -= btodb(deallocated);
278 ip->i_flag |= IN_CHANGE | IN_UPDATE;
279 }
280 return (error);
281 }
282