xref: /dragonfly/sys/vfs/ext2fs/ext2_balloc.c (revision e5b38eb588d517bc6924dd57a647f58246c5217b)
1 /*-
2  *  modified for Lites 1.1
3  *
4  *  Aug 1995, Godmar Back (gback@cs.utah.edu)
5  *  University of Utah, Department of Computer Science
6  */
7 /*-
8  * SPDX-License-Identifier: BSD-3-Clause
9  *
10  * Copyright (c) 1982, 1986, 1989, 1993
11  *        The Regents of the University of California.  All rights reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  *        @(#)ffs_balloc.c    8.4 (Berkeley) 9/23/93
38  * $FreeBSD$
39  */
40 
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/endian.h>
44 #include <sys/bio.h>
45 #include <sys/buf2.h>
46 #include <sys/limits.h>
47 #include <sys/lock.h>
48 #include <sys/mount.h>
49 #include <sys/vnode.h>
50 #include <sys/mutex2.h>
51 
52 #include <vfs/ext2fs/fs.h>
53 #include <vfs/ext2fs/inode.h>
54 #include <vfs/ext2fs/ext2fs.h>
55 #include <vfs/ext2fs/ext2_dinode.h>
56 #include <vfs/ext2fs/ext2_extern.h>
57 #include <vfs/ext2fs/ext2_mount.h>
58 
59 static int
ext2_ext_balloc(struct inode * ip,uint32_t lbn,int size,struct ucred * cred,struct buf ** bpp,int flags)60 ext2_ext_balloc(struct inode *ip, uint32_t lbn, int size,
61     struct ucred *cred, struct buf **bpp, int flags)
62 {
63           return (EINVAL);
64 }
65 
66 /*
67  * Balloc defines the structure of filesystem storage
68  * by allocating the physical blocks on a device given
69  * the inode and the logical block number in a file.
70  */
71 int
ext2_balloc(struct inode * ip,e2fs_lbn_t lbn,int size,struct ucred * cred,struct buf ** bpp,int flags)72 ext2_balloc(struct inode *ip, e2fs_lbn_t lbn, int size, struct ucred *cred,
73     struct buf **bpp, int flags)
74 {
75           struct m_ext2fs *fs;
76           struct ext2mount *ump;
77           struct buf *bp, *nbp;
78           struct vnode *vp = ITOV(ip);
79           struct indir indirs[EXT2_NIADDR + 2];
80           e4fs_daddr_t nb, newb;
81           e2fs_daddr_t *bap, pref;
82           int num, i, error;
83 
84           *bpp = NULL;
85           if (lbn < 0)
86                     return (EFBIG);
87           fs = ip->i_e2fs;
88           ump = ip->i_ump;
89 
90           /*
91            * check if this is a sequential block allocation.
92            * If so, increment next_alloc fields to allow ext2_blkpref
93            * to make a good guess
94            */
95           if (lbn == ip->i_next_alloc_block + 1) {
96                     ip->i_next_alloc_block++;
97                     ip->i_next_alloc_goal++;
98           }
99 
100           if (ip->i_flag & IN_E4EXTENTS)
101                     return (ext2_ext_balloc(ip, lbn, size, cred, bpp, flags));
102 
103           /*
104            * The first EXT2_NDADDR blocks are direct blocks
105            */
106           if (lbn < EXT2_NDADDR) {
107                     nb = ip->i_db[lbn];
108                     /*
109                      * no new block is to be allocated, and no need to expand
110                      * the file
111                      */
112                     if (nb != 0) {
113                               error = bread(vp, lblktodoff(fs, lbn), fs->e2fs_bsize,
114                                   &bp);
115                               if (error) {
116                                         brelse(bp);
117                                         return (error);
118                               }
119                               bp->b_bio2.bio_offset = fsbtodoff(fs, nb);
120                               if (ip->i_size >= (lbn + 1) * fs->e2fs_bsize) {
121                                         *bpp = bp;
122                                         return (0);
123                               }
124                     } else {
125                               EXT2_LOCK(ump);
126                               error = ext2_alloc(ip, lbn,
127                                   ext2_blkpref(ip, lbn, (int)lbn, &ip->i_db[0], 0),
128                                   fs->e2fs_bsize, cred, &newb);
129                               if (error)
130                                         return (error);
131                               /*
132                                * If the newly allocated block exceeds 32-bit limit,
133                                * we can not use it in file block maps.
134                                */
135                               if (newb > UINT_MAX)
136                                         return (EFBIG);
137                               bp = getblk(vp, lblktodoff(fs, lbn), fs->e2fs_bsize, 0, 0);
138                               bp->b_bio2.bio_offset = fsbtodoff(fs, newb);
139                               if (flags & BA_CLRBUF)
140                                         vfs_bio_clrbuf(bp);
141                     }
142                     ip->i_db[lbn] = dofftofsb(fs, bp->b_bio2.bio_offset);
143                     ip->i_flag |= IN_CHANGE | IN_UPDATE;
144                     *bpp = bp;
145                     return (0);
146           }
147           /*
148            * Determine the number of levels of indirection.
149            */
150           pref = 0;
151           if ((error = ext2_getlbns(vp, lbn, indirs, &num)) != 0)
152                     return (error);
153 #ifdef INVARIANTS
154           if (num < 1)
155                     panic("ext2_balloc: ext2_getlbns returned indirect block");
156 #endif
157           /*
158            * Fetch the first indirect block allocating if necessary.
159            */
160           --num;
161           nb = ip->i_ib[indirs[0].in_off];
162           if (nb == 0) {
163                     EXT2_LOCK(ump);
164                     pref = ext2_blkpref(ip, lbn, indirs[0].in_off +
165                         EXT2_NDIR_BLOCKS, &ip->i_db[0], 0);
166                     if ((error = ext2_alloc(ip, lbn, pref, fs->e2fs_bsize, cred,
167                         &newb)))
168                               return (error);
169                     if (newb > UINT_MAX)
170                               return (EFBIG);
171                     nb = newb;
172                     bp = getblk(vp, lblktodoff(fs, indirs[1].in_lbn),
173                         fs->e2fs_bsize, 0, 0);
174                     bp->b_bio2.bio_offset = fsbtodoff(fs, newb);
175                     vfs_bio_clrbuf(bp);
176                     /*
177                      * Write synchronously so that indirect blocks
178                      * never point at garbage.
179                      */
180                     if ((error = bwrite(bp)) != 0) {
181                               ext2_blkfree(ip, nb, fs->e2fs_bsize);
182                               return (error);
183                     }
184                     ip->i_ib[indirs[0].in_off] = newb;
185                     ip->i_flag |= IN_CHANGE | IN_UPDATE;
186           }
187           /*
188            * Fetch through the indirect blocks, allocating as necessary.
189            */
190           for (i = 1;;) {
191                     error = bread(vp, lblktodoff(fs, indirs[i].in_lbn),
192                         (int)fs->e2fs_bsize, &bp);
193                     if (error) {
194                               brelse(bp);
195                               return (error);
196                     }
197                     bap = (e2fs_daddr_t *)bp->b_data;
198                     nb = le32toh(bap[indirs[i].in_off]);
199                     if (i == num)
200                               break;
201                     i += 1;
202                     if (nb != 0) {
203                               bqrelse(bp);
204                               continue;
205                     }
206                     EXT2_LOCK(ump);
207                     if (pref == 0)
208                               pref = ext2_blkpref(ip, lbn, indirs[i].in_off, bap,
209                                   lblkno(fs, bp->b_loffset));
210                     error = ext2_alloc(ip, lbn, pref, (int)fs->e2fs_bsize, cred, &newb);
211                     if (error) {
212                               brelse(bp);
213                               return (error);
214                     }
215                     if (newb > UINT_MAX) {
216                               brelse(bp);
217                               return (EFBIG);
218                     }
219                     nb = newb;
220                     nbp = getblk(vp, lblktodoff(fs, indirs[i].in_lbn),
221                         fs->e2fs_bsize, 0, 0);
222                     nbp->b_bio2.bio_offset = fsbtodoff(fs, nb);
223                     vfs_bio_clrbuf(nbp);
224                     /*
225                      * Write synchronously so that indirect blocks
226                      * never point at garbage.
227                      */
228                     if ((error = bwrite(nbp)) != 0) {
229                               ext2_blkfree(ip, nb, fs->e2fs_bsize);
230                               brelse(bp);
231                               return (error);
232                     }
233                     bap[indirs[i - 1].in_off] = htole32(nb);
234                     /*
235                      * If required, write synchronously, otherwise use
236                      * delayed write.
237                      */
238                     if (flags & IO_SYNC) {
239                               bwrite(bp);
240                     } else {
241                               if (bp->b_bufsize == fs->e2fs_bsize)
242                                         bp->b_flags |= B_CLUSTEROK;
243                               bdwrite(bp);
244                     }
245           }
246           /*
247            * Get the data block, allocating if necessary.
248            */
249           if (nb == 0) {
250                     EXT2_LOCK(ump);
251                     pref = ext2_blkpref(ip, lbn, indirs[i].in_off, &bap[0],
252                         lblkno(fs, bp->b_loffset));
253                     if ((error = ext2_alloc(ip,
254                         lbn, pref, (int)fs->e2fs_bsize, cred, &newb)) != 0) {
255                               brelse(bp);
256                               return (error);
257                     }
258                     if (newb > UINT_MAX) {
259                               brelse(bp);
260                               return (EFBIG);
261                     }
262                     nb = newb;
263                     nbp = getblk(vp, lblktodoff(fs, lbn), fs->e2fs_bsize, 0, 0);
264                     nbp->b_bio2.bio_offset = fsbtodoff(fs, nb);
265                     if (flags & BA_CLRBUF)
266                               vfs_bio_clrbuf(nbp);
267                     bap[indirs[i].in_off] = htole32(nb);
268                     /*
269                      * If required, write synchronously, otherwise use
270                      * delayed write.
271                      */
272                     if (flags & IO_SYNC) {
273                               bwrite(bp);
274                     } else {
275                               if (bp->b_bufsize == fs->e2fs_bsize)
276                                         bp->b_flags |= B_CLUSTEROK;
277                               bdwrite(bp);
278                     }
279                     *bpp = nbp;
280                     return (0);
281           }
282           brelse(bp);
283           if (flags & BA_CLRBUF) {
284                     int seqcount = (flags & BA_SEQMASK) >> BA_SEQSHIFT;
285 
286                     if (seqcount && (vp->v_mount->mnt_flag & MNT_NOCLUSTERR) == 0) {
287                               error = cluster_read(vp, ip->i_size,
288                                   lblktodoff(fs, lbn), (int)fs->e2fs_bsize,
289                                   MAXBSIZE, (size_t)seqcount, &nbp);
290                     } else {
291                               error = bread(vp, lblktodoff(fs, lbn),
292                                   (int)fs->e2fs_bsize, &nbp);
293                     }
294                     if (error) {
295                               brelse(nbp);
296                               return (error);
297                     }
298           } else {
299                     nbp = getblk(vp, lblktodoff(fs, lbn), fs->e2fs_bsize, 0, 0);
300                     nbp->b_bio2.bio_offset = fsbtodoff(fs, nb);
301           }
302           *bpp = nbp;
303           return (0);
304 }
305