Coverage Report

Created: 2026-04-29 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/e2fsprogs/lib/ext2fs/fallocate.c
Line
Count
Source
1
/*
2
 * fallocate.c -- Allocate large chunks of file.
3
 *
4
 * Copyright (C) 2014 Oracle.
5
 *
6
 * %Begin-Header%
7
 * This file may be redistributed under the terms of the GNU Library
8
 * General Public License, version 2.
9
 * %End-Header%
10
 */
11
12
#include "config.h"
13
14
#if HAVE_SYS_TYPES_H
15
#include <sys/types.h>
16
#endif
17
18
#include "ext2_fs.h"
19
#include "ext2fs.h"
20
0
#define min(a, b) ((a) < (b) ? (a) : (b))
21
22
#undef DEBUG
23
24
#ifdef DEBUG
25
# define dbg_printf(f, a...)  do {printf(f, ## a); fflush(stdout); } while (0)
26
#else
27
# define dbg_printf(f, a...)
28
#endif
29
30
/*
31
 * Extent-based fallocate code.
32
 *
33
 * Find runs of unmapped logical blocks by starting at start and walking the
34
 * extents until we reach the end of the range we want.
35
 *
36
 * For each run of unmapped blocks, try to find the extents on either side of
37
 * the range.  If there's a left extent that can grow by at least a cluster and
38
 * there are lblocks between start and the next lcluster after start, see if
39
 * there's an implied cluster allocation; if so, zero the blocks (if the left
40
 * extent is initialized) and adjust the extent.  Ditto for the blocks between
41
 * the end of the last full lcluster and end, if there's a right extent.
42
 *
43
 * Try to attach as much as we can to the left extent, then try to attach as
44
 * much as we can to the right extent.  For the remainder, try to allocate the
45
 * whole range; map in whatever we get; and repeat until we're done.
46
 *
47
 * To attach to a left extent, figure out the maximum amount we can add to the
48
 * extent and try to allocate that much, and append if successful.  To attach
49
 * to a right extent, figure out the max we can add to the extent, try to
50
 * allocate that much, and prepend if successful.
51
 *
52
 * We need an alloc_range function that tells us how much we can allocate given
53
 * a maximum length and one of a suggested start, a fixed start, or a fixed end
54
 * point.
55
 *
56
 * Every time we modify the extent tree we also need to update the block stats.
57
 *
58
 * At the end, update i_blocks and i_size appropriately.
59
 */
60
61
static void dbg_print_extent(const char *desc EXT2FS_ATTR((unused)),
62
    const struct ext2fs_extent *extent EXT2FS_ATTR((unused)))
63
0
{
64
#ifdef DEBUG
65
  if (desc)
66
    printf("%s: ", desc);
67
  printf("extent: lblk %llu--%llu, len %u, pblk %llu, flags: ",
68
         extent->e_lblk, extent->e_lblk + extent->e_len - 1,
69
         extent->e_len, extent->e_pblk);
70
  if (extent->e_flags & EXT2_EXTENT_FLAGS_LEAF)
71
    fputs("LEAF ", stdout);
72
  if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT)
73
    fputs("UNINIT ", stdout);
74
  if (extent->e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT)
75
    fputs("2ND_VISIT ", stdout);
76
  if (!extent->e_flags)
77
    fputs("(none)", stdout);
78
  fputc('\n', stdout);
79
  fflush(stdout);
80
#endif
81
0
}
82
83
static errcode_t claim_range(ext2_filsys fs, struct ext2_inode *inode,
84
           blk64_t blk, blk64_t len)
85
0
{
86
0
  blk64_t clusters;
87
88
0
  clusters = (len + EXT2FS_CLUSTER_RATIO(fs) - 1) /
89
0
       EXT2FS_CLUSTER_RATIO(fs);
90
0
  ext2fs_block_alloc_stats_range(fs, blk,
91
0
      clusters * EXT2FS_CLUSTER_RATIO(fs), +1);
92
0
  return ext2fs_iblk_add_blocks(fs, inode, clusters);
93
0
}
94
95
static errcode_t ext_falloc_helper(ext2_filsys fs,
96
           int flags,
97
           ext2_ino_t ino,
98
           struct ext2_inode *inode,
99
           ext2_extent_handle_t handle,
100
           struct ext2fs_extent *left_ext,
101
           struct ext2fs_extent *right_ext,
102
           blk64_t range_start, blk64_t range_len,
103
           blk64_t alloc_goal)
104
0
{
105
0
  struct ext2fs_extent  newex, ex;
106
0
  int     op;
107
0
  blk64_t     fillable, pblk, plen, x, y;
108
0
  blk64_t     eof_blk = 0, cluster_fill = 0;
109
0
  errcode_t   err;
110
0
  blk_t     max_extent_len, max_uninit_len, max_init_len;
111
112
#ifdef DEBUG
113
  printf("%s: ", __func__);
114
  if (left_ext)
115
    printf("left_ext=%llu--%llu, ", left_ext->e_lblk,
116
           left_ext->e_lblk + left_ext->e_len - 1);
117
  if (right_ext)
118
    printf("right_ext=%llu--%llu, ", right_ext->e_lblk,
119
           right_ext->e_lblk + right_ext->e_len - 1);
120
  printf("start=%llu len=%llu, goal=%llu\n", range_start, range_len,
121
         alloc_goal);
122
  fflush(stdout);
123
#endif
124
  /* Can't create initialized extents past EOF? */
125
0
  if (!(flags & EXT2_FALLOCATE_INIT_BEYOND_EOF))
126
0
    eof_blk = EXT2_I_SIZE(inode) / fs->blocksize;
127
128
  /* The allocation goal must be as far into a cluster as range_start. */
129
0
  alloc_goal = (alloc_goal & ~EXT2FS_CLUSTER_MASK(fs)) |
130
0
         (range_start & EXT2FS_CLUSTER_MASK(fs));
131
132
0
  max_uninit_len = EXT_UNINIT_MAX_LEN & ~EXT2FS_CLUSTER_MASK(fs);
133
0
  max_init_len = EXT_INIT_MAX_LEN & ~EXT2FS_CLUSTER_MASK(fs);
134
135
  /* We must lengthen the left extent to the end of the cluster */
136
0
  if (left_ext && EXT2FS_CLUSTER_RATIO(fs) > 1) {
137
    /* How many more blocks can be attached to left_ext? */
138
0
    if (left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)
139
0
      fillable = max_uninit_len - left_ext->e_len;
140
0
    else
141
0
      fillable = max_init_len - left_ext->e_len;
142
143
0
    if (fillable > range_len)
144
0
      fillable = range_len;
145
0
    if (fillable == 0)
146
0
      goto expand_right;
147
148
    /*
149
     * If range_start isn't on a cluster boundary, try an
150
     * implied cluster allocation for left_ext.
151
     */
152
0
    cluster_fill = EXT2FS_CLUSTER_RATIO(fs) -
153
0
             (range_start & EXT2FS_CLUSTER_MASK(fs));
154
0
    cluster_fill &= EXT2FS_CLUSTER_MASK(fs);
155
0
    if (cluster_fill == 0)
156
0
      goto expand_right;
157
158
0
    if (cluster_fill > fillable)
159
0
      cluster_fill = fillable;
160
161
    /* Don't expand an initialized left_ext beyond EOF */
162
0
    if (!(flags & EXT2_FALLOCATE_INIT_BEYOND_EOF)) {
163
0
      x = left_ext->e_lblk + left_ext->e_len - 1;
164
0
      dbg_printf("%s: lend=%llu newlend=%llu eofblk=%llu\n",
165
0
           __func__, x, x + cluster_fill, eof_blk);
166
0
      if (eof_blk >= x && eof_blk <= x + cluster_fill)
167
0
        cluster_fill = eof_blk - x;
168
0
      if (cluster_fill == 0)
169
0
        goto expand_right;
170
0
    }
171
172
0
    err = ext2fs_extent_goto(handle, left_ext->e_lblk);
173
0
    if (err)
174
0
      goto expand_right;
175
0
    left_ext->e_len += cluster_fill;
176
0
    range_start += cluster_fill;
177
0
    range_len -= cluster_fill;
178
0
    alloc_goal += cluster_fill;
179
180
0
    dbg_print_extent("ext_falloc clus left+", left_ext);
181
0
    err = ext2fs_extent_replace(handle, 0, left_ext);
182
0
    if (err)
183
0
      goto out;
184
0
    err = ext2fs_extent_fix_parents(handle);
185
0
    if (err)
186
0
      goto out;
187
188
    /* Zero blocks */
189
0
    if (!(left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)) {
190
0
      err = ext2fs_zero_blocks2(fs, left_ext->e_pblk +
191
0
              left_ext->e_len -
192
0
              cluster_fill, cluster_fill,
193
0
              NULL, NULL);
194
0
      if (err)
195
0
        goto out;
196
0
    }
197
0
  }
198
199
0
expand_right:
200
  /* We must lengthen the right extent to the beginning of the cluster */
201
0
  if (right_ext && EXT2FS_CLUSTER_RATIO(fs) > 1) {
202
    /* How much can we attach to right_ext? */
203
0
    if (right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)
204
0
      fillable = max_uninit_len - right_ext->e_len;
205
0
    else
206
0
      fillable = max_init_len - right_ext->e_len;
207
208
0
    if (fillable > range_len)
209
0
      fillable = range_len;
210
0
    if (fillable == 0)
211
0
      goto try_merge;
212
213
    /*
214
     * If range_end isn't on a cluster boundary, try an implied
215
     * cluster allocation for right_ext.
216
     */
217
0
    cluster_fill = right_ext->e_lblk & EXT2FS_CLUSTER_MASK(fs);
218
0
    if (cluster_fill == 0)
219
0
      goto try_merge;
220
221
0
    err = ext2fs_extent_goto(handle, right_ext->e_lblk);
222
0
    if (err)
223
0
      goto out;
224
225
0
    if (cluster_fill > fillable)
226
0
      cluster_fill = fillable;
227
0
    right_ext->e_lblk -= cluster_fill;
228
0
    right_ext->e_pblk -= cluster_fill;
229
0
    right_ext->e_len += cluster_fill;
230
0
    range_len -= cluster_fill;
231
232
0
    dbg_print_extent("ext_falloc clus right+", right_ext);
233
0
    err = ext2fs_extent_replace(handle, 0, right_ext);
234
0
    if (err)
235
0
      goto out;
236
0
    err = ext2fs_extent_fix_parents(handle);
237
0
    if (err)
238
0
      goto out;
239
240
    /* Zero blocks if necessary */
241
0
    if (!(right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)) {
242
0
      err = ext2fs_zero_blocks2(fs, right_ext->e_pblk,
243
0
              cluster_fill, NULL, NULL);
244
0
      if (err)
245
0
        goto out;
246
0
    }
247
0
  }
248
249
0
try_merge:
250
  /* Merge both extents together, perhaps? */
251
0
  if (left_ext && right_ext) {
252
    /* Are the two extents mergeable? */
253
0
    if ((left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT) !=
254
0
        (right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT))
255
0
      goto try_left;
256
257
    /* User requires init/uninit but extent is uninit/init. */
258
0
    if (((flags & EXT2_FALLOCATE_FORCE_INIT) &&
259
0
         (left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)) ||
260
0
        ((flags & EXT2_FALLOCATE_FORCE_UNINIT) &&
261
0
         !(left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)))
262
0
      goto try_left;
263
264
    /*
265
     * Skip initialized extent unless user wants to zero blocks
266
     * or requires init extent.
267
     */
268
0
    if (!(left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
269
0
        (!(flags & EXT2_FALLOCATE_ZERO_BLOCKS) ||
270
0
         !(flags & EXT2_FALLOCATE_FORCE_INIT)))
271
0
      goto try_left;
272
273
    /* Will it even fit? */
274
0
    x = left_ext->e_len + range_len + right_ext->e_len;
275
0
    if (x > (left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT ?
276
0
        max_uninit_len : max_init_len))
277
0
      goto try_left;
278
279
    /* Would they even be physically contiguous if merged? */
280
0
    if (left_ext->e_pblk + left_ext->e_len + range_len !=
281
0
        right_ext->e_pblk)
282
0
      goto try_left;
283
284
0
    err = ext2fs_extent_goto(handle, left_ext->e_lblk);
285
0
    if (err)
286
0
      goto try_left;
287
288
    /* Allocate blocks */
289
0
    y = left_ext->e_pblk + left_ext->e_len;
290
0
    err = ext2fs_new_range(fs, EXT2_NEWRANGE_FIXED_GOAL |
291
0
               EXT2_NEWRANGE_MIN_LENGTH, y,
292
0
               right_ext->e_pblk - y + 1, NULL,
293
0
               &pblk, &plen);
294
0
    if (err)
295
0
      goto try_left;
296
0
    if (pblk + plen != right_ext->e_pblk)
297
0
      goto try_left;
298
0
    err = claim_range(fs, inode, pblk, plen);
299
0
    if (err)
300
0
      goto out;
301
302
    /* Modify extents */
303
0
    left_ext->e_len = x;
304
0
    dbg_print_extent("ext_falloc merge", left_ext);
305
0
    err = ext2fs_extent_replace(handle, 0, left_ext);
306
0
    if (err)
307
0
      goto out;
308
0
    err = ext2fs_extent_fix_parents(handle);
309
0
    if (err)
310
0
      goto out;
311
0
    err = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_LEAF, &newex);
312
0
    if (err)
313
0
      goto out;
314
0
    err = ext2fs_extent_delete(handle, 0);
315
0
    if (err)
316
0
      goto out;
317
0
    err = ext2fs_extent_fix_parents(handle);
318
0
    if (err)
319
0
      goto out;
320
0
    *right_ext = *left_ext;
321
322
    /* Zero blocks */
323
0
    if (!(left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
324
0
        (flags & EXT2_FALLOCATE_ZERO_BLOCKS)) {
325
0
      err = ext2fs_zero_blocks2(fs, range_start, range_len,
326
0
              NULL, NULL);
327
0
      if (err)
328
0
        goto out;
329
0
    }
330
331
0
    return 0;
332
0
  }
333
334
0
try_left:
335
  /* Extend the left extent */
336
0
  if (left_ext) {
337
    /* How many more blocks can be attached to left_ext? */
338
0
    if (left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)
339
0
      fillable = max_uninit_len - left_ext->e_len;
340
0
    else if (flags & EXT2_FALLOCATE_ZERO_BLOCKS)
341
0
      fillable = max_init_len - left_ext->e_len;
342
0
    else
343
0
      fillable = 0;
344
345
    /* User requires init/uninit but extent is uninit/init. */
346
0
    if (((flags & EXT2_FALLOCATE_FORCE_INIT) &&
347
0
         (left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)) ||
348
0
        ((flags & EXT2_FALLOCATE_FORCE_UNINIT) &&
349
0
         !(left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)))
350
0
      goto try_right;
351
352
0
    if (fillable > range_len)
353
0
      fillable = range_len;
354
355
    /* Don't expand an initialized left_ext beyond EOF */
356
0
    x = left_ext->e_lblk + left_ext->e_len - 1;
357
0
    if (!(flags & EXT2_FALLOCATE_INIT_BEYOND_EOF)) {
358
0
      dbg_printf("%s: lend=%llu newlend=%llu eofblk=%llu\n",
359
0
           __func__, x, x + fillable, eof_blk);
360
0
      if (eof_blk >= x && eof_blk <= x + fillable)
361
0
        fillable = eof_blk - x;
362
0
    }
363
364
0
    if (fillable == 0)
365
0
      goto try_right;
366
367
    /* Test if the right edge of the range is already mapped? */
368
0
    if (EXT2FS_CLUSTER_RATIO(fs) > 1) {
369
0
      err = ext2fs_map_cluster_block(fs, ino, inode,
370
0
          x + fillable, &pblk);
371
0
      if (err)
372
0
        goto out;
373
0
      if (pblk)
374
0
        fillable -= 1 + ((x + fillable)
375
0
             & EXT2FS_CLUSTER_MASK(fs));
376
0
      if (fillable == 0)
377
0
        goto try_right;
378
0
    }
379
380
    /* Allocate range of blocks */
381
0
    x = left_ext->e_pblk + left_ext->e_len;
382
0
    err = ext2fs_new_range(fs, EXT2_NEWRANGE_FIXED_GOAL |
383
0
        EXT2_NEWRANGE_MIN_LENGTH,
384
0
        x, fillable, NULL, &pblk, &plen);
385
0
    if (err)
386
0
      goto try_right;
387
0
    err = claim_range(fs, inode, pblk, plen);
388
0
    if (err)
389
0
      goto out;
390
391
    /* Modify left_ext */
392
0
    err = ext2fs_extent_goto(handle, left_ext->e_lblk);
393
0
    if (err)
394
0
      goto out;
395
0
    range_start += plen;
396
0
    range_len -= plen;
397
0
    left_ext->e_len += plen;
398
0
    dbg_print_extent("ext_falloc left+", left_ext);
399
0
    err = ext2fs_extent_replace(handle, 0, left_ext);
400
0
    if (err)
401
0
      goto out;
402
0
    err = ext2fs_extent_fix_parents(handle);
403
0
    if (err)
404
0
      goto out;
405
406
    /* Zero blocks if necessary */
407
0
    if (!(left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
408
0
        (flags & EXT2_FALLOCATE_ZERO_BLOCKS)) {
409
0
      err = ext2fs_zero_blocks2(fs, pblk, plen, NULL, NULL);
410
0
      if (err)
411
0
        goto out;
412
0
    }
413
0
  }
414
415
0
try_right:
416
  /* Extend the right extent */
417
0
  if (right_ext) {
418
    /* How much can we attach to right_ext? */
419
0
    if (right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)
420
0
      fillable = max_uninit_len - right_ext->e_len;
421
0
    else if (flags & EXT2_FALLOCATE_ZERO_BLOCKS)
422
0
      fillable = max_init_len - right_ext->e_len;
423
0
    else
424
0
      fillable = 0;
425
426
    /* User requires init/uninit but extent is uninit/init. */
427
0
    if (((flags & EXT2_FALLOCATE_FORCE_INIT) &&
428
0
         (right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)) ||
429
0
        ((flags & EXT2_FALLOCATE_FORCE_UNINIT) &&
430
0
         !(right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)))
431
0
      goto try_anywhere;
432
433
0
    if (fillable > range_len)
434
0
      fillable = range_len;
435
0
    if (fillable == 0)
436
0
      goto try_anywhere;
437
438
    /* Test if the left edge of the range is already mapped? */
439
0
    if (EXT2FS_CLUSTER_RATIO(fs) > 1) {
440
0
      err = ext2fs_map_cluster_block(fs, ino, inode,
441
0
          right_ext->e_lblk - fillable, &pblk);
442
0
      if (err)
443
0
        goto out;
444
0
      if (pblk)
445
0
        fillable -= EXT2FS_CLUSTER_RATIO(fs) -
446
0
            ((right_ext->e_lblk - fillable)
447
0
             & EXT2FS_CLUSTER_MASK(fs));
448
0
      if (fillable == 0)
449
0
        goto try_anywhere;
450
0
    }
451
452
    /*
453
     * FIXME: It would be nice if we could handle allocating a
454
     * variable range from a fixed end point instead of just
455
     * skipping to the general allocator if the whole range is
456
     * unavailable.
457
     */
458
0
    err = ext2fs_new_range(fs, EXT2_NEWRANGE_FIXED_GOAL |
459
0
        EXT2_NEWRANGE_MIN_LENGTH,
460
0
        right_ext->e_pblk - fillable,
461
0
        fillable, NULL, &pblk, &plen);
462
0
    if (err)
463
0
      goto try_anywhere;
464
0
    err = claim_range(fs, inode,
465
0
            pblk & ~EXT2FS_CLUSTER_MASK(fs),
466
0
            plen + (pblk & EXT2FS_CLUSTER_MASK(fs)));
467
0
    if (err)
468
0
      goto out;
469
470
    /* Modify right_ext */
471
0
    err = ext2fs_extent_goto(handle, right_ext->e_lblk);
472
0
    if (err)
473
0
      goto out;
474
0
    range_len -= plen;
475
0
    right_ext->e_lblk -= plen;
476
0
    right_ext->e_pblk -= plen;
477
0
    right_ext->e_len += plen;
478
0
    dbg_print_extent("ext_falloc right+", right_ext);
479
0
    err = ext2fs_extent_replace(handle, 0, right_ext);
480
0
    if (err)
481
0
      goto out;
482
0
    err = ext2fs_extent_fix_parents(handle);
483
0
    if (err)
484
0
      goto out;
485
486
    /* Zero blocks if necessary */
487
0
    if (!(right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
488
0
        (flags & EXT2_FALLOCATE_ZERO_BLOCKS)) {
489
0
      err = ext2fs_zero_blocks2(fs, pblk,
490
0
          plen + cluster_fill, NULL, NULL);
491
0
      if (err)
492
0
        goto out;
493
0
    }
494
0
  }
495
496
0
try_anywhere:
497
  /* Try implied cluster alloc on the left and right ends */
498
0
  if (range_len > 0 && (range_start & EXT2FS_CLUSTER_MASK(fs))) {
499
0
    cluster_fill = EXT2FS_CLUSTER_RATIO(fs) -
500
0
             (range_start & EXT2FS_CLUSTER_MASK(fs));
501
0
    cluster_fill &= EXT2FS_CLUSTER_MASK(fs);
502
0
    if (cluster_fill > range_len)
503
0
      cluster_fill = range_len;
504
0
    newex.e_lblk = range_start;
505
0
    err = ext2fs_map_cluster_block(fs, ino, inode, newex.e_lblk,
506
0
                 &pblk);
507
0
    if (err)
508
0
      goto out;
509
0
    if (pblk == 0)
510
0
      goto try_right_implied;
511
0
    newex.e_pblk = pblk;
512
0
    newex.e_len = cluster_fill;
513
0
    newex.e_flags = (flags & EXT2_FALLOCATE_FORCE_INIT ? 0 :
514
0
         EXT2_EXTENT_FLAGS_UNINIT);
515
0
    dbg_print_extent("ext_falloc iclus left+", &newex);
516
0
    ext2fs_extent_goto(handle, newex.e_lblk);
517
0
    err = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT,
518
0
          &ex);
519
0
    if (err == EXT2_ET_NO_CURRENT_NODE)
520
0
      ex.e_lblk = 0;
521
0
    else if (err)
522
0
      goto out;
523
524
0
    if (ex.e_lblk > newex.e_lblk)
525
0
      op = 0; /* insert before */
526
0
    else
527
0
      op = EXT2_EXTENT_INSERT_AFTER;
528
0
    dbg_printf("%s: inserting %s lblk %llu newex=%llu\n",
529
0
         __func__, op ? "after" : "before", ex.e_lblk,
530
0
         newex.e_lblk);
531
0
    err = ext2fs_extent_insert(handle, op, &newex);
532
0
    if (err)
533
0
      goto out;
534
0
    err = ext2fs_extent_fix_parents(handle);
535
0
    if (err)
536
0
      goto out;
537
538
0
    if (!(newex.e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
539
0
        (flags & EXT2_FALLOCATE_ZERO_BLOCKS)) {
540
0
      err = ext2fs_zero_blocks2(fs, newex.e_pblk,
541
0
              newex.e_len, NULL, NULL);
542
0
      if (err)
543
0
        goto out;
544
0
    }
545
546
0
    range_start += cluster_fill;
547
0
    range_len -= cluster_fill;
548
0
  }
549
550
0
try_right_implied:
551
0
  y = range_start + range_len;
552
0
  if (range_len > 0 && (y & EXT2FS_CLUSTER_MASK(fs))) {
553
0
    cluster_fill = y & EXT2FS_CLUSTER_MASK(fs);
554
0
    if (cluster_fill > range_len)
555
0
      cluster_fill = range_len;
556
0
    newex.e_lblk = y & ~EXT2FS_CLUSTER_MASK(fs);
557
0
    err = ext2fs_map_cluster_block(fs, ino, inode, newex.e_lblk,
558
0
                 &pblk);
559
0
    if (err)
560
0
      goto out;
561
0
    if (pblk == 0)
562
0
      goto no_implied;
563
0
    newex.e_pblk = pblk;
564
0
    newex.e_len = cluster_fill;
565
0
    newex.e_flags = (flags & EXT2_FALLOCATE_FORCE_INIT ? 0 :
566
0
         EXT2_EXTENT_FLAGS_UNINIT);
567
0
    dbg_print_extent("ext_falloc iclus right+", &newex);
568
0
    ext2fs_extent_goto(handle, newex.e_lblk);
569
0
    err = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT,
570
0
          &ex);
571
0
    if (err == EXT2_ET_NO_CURRENT_NODE)
572
0
      ex.e_lblk = 0;
573
0
    else if (err)
574
0
      goto out;
575
576
0
    if (ex.e_lblk > newex.e_lblk)
577
0
      op = 0; /* insert before */
578
0
    else
579
0
      op = EXT2_EXTENT_INSERT_AFTER;
580
0
    dbg_printf("%s: inserting %s lblk %llu newex=%llu\n",
581
0
         __func__, op ? "after" : "before", ex.e_lblk,
582
0
         newex.e_lblk);
583
0
    err = ext2fs_extent_insert(handle, op, &newex);
584
0
    if (err)
585
0
      goto out;
586
0
    err = ext2fs_extent_fix_parents(handle);
587
0
    if (err)
588
0
      goto out;
589
590
0
    if (!(newex.e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
591
0
        (flags & EXT2_FALLOCATE_ZERO_BLOCKS)) {
592
0
      err = ext2fs_zero_blocks2(fs, newex.e_pblk,
593
0
              newex.e_len, NULL, NULL);
594
0
      if (err)
595
0
        goto out;
596
0
    }
597
598
0
    range_len -= cluster_fill;
599
0
  }
600
601
0
no_implied:
602
0
  if (range_len == 0)
603
0
    return 0;
604
605
0
  newex.e_lblk = range_start;
606
0
  if (flags & EXT2_FALLOCATE_FORCE_INIT) {
607
0
    max_extent_len = max_init_len;
608
0
    newex.e_flags = 0;
609
0
  } else {
610
0
    max_extent_len = max_uninit_len;
611
0
    newex.e_flags = EXT2_EXTENT_FLAGS_UNINIT;
612
0
  }
613
0
  pblk = alloc_goal;
614
0
  y = range_len;
615
0
  for (x = 0; x < y;) {
616
0
    cluster_fill = newex.e_lblk & EXT2FS_CLUSTER_MASK(fs);
617
0
    fillable = min(range_len + cluster_fill, max_extent_len);
618
0
    err = ext2fs_new_range(fs, 0, pblk & ~EXT2FS_CLUSTER_MASK(fs),
619
0
               fillable,
620
0
               NULL, &pblk, &plen);
621
0
    if (err)
622
0
      goto out;
623
0
    err = claim_range(fs, inode, pblk, plen);
624
0
    if (err)
625
0
      goto out;
626
627
    /* Create extent */
628
0
    newex.e_pblk = pblk + cluster_fill;
629
0
    newex.e_len = plen - cluster_fill;
630
0
    dbg_print_extent("ext_falloc create", &newex);
631
0
    ext2fs_extent_goto(handle, newex.e_lblk);
632
0
    err = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT,
633
0
          &ex);
634
0
    if (err == EXT2_ET_NO_CURRENT_NODE)
635
0
      ex.e_lblk = 0;
636
0
    else if (err)
637
0
      goto out;
638
639
0
    if (ex.e_lblk > newex.e_lblk)
640
0
      op = 0; /* insert before */
641
0
    else
642
0
      op = EXT2_EXTENT_INSERT_AFTER;
643
0
    dbg_printf("%s: inserting %s lblk %llu newex=%llu\n",
644
0
         __func__, op ? "after" : "before", ex.e_lblk,
645
0
         newex.e_lblk);
646
0
    err = ext2fs_extent_insert(handle, op, &newex);
647
0
    if (err)
648
0
      goto out;
649
0
    err = ext2fs_extent_fix_parents(handle);
650
0
    if (err)
651
0
      goto out;
652
653
0
    if (!(newex.e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
654
0
        (flags & EXT2_FALLOCATE_ZERO_BLOCKS)) {
655
0
      err = ext2fs_zero_blocks2(fs, pblk, plen, NULL, NULL);
656
0
      if (err)
657
0
        goto out;
658
0
    }
659
660
    /* Update variables at end of loop */
661
0
    x += plen - cluster_fill;
662
0
    range_len -= plen - cluster_fill;
663
0
    newex.e_lblk += plen - cluster_fill;
664
0
    pblk += plen - cluster_fill;
665
0
    if (pblk >= ext2fs_blocks_count(fs->super))
666
0
      pblk = fs->super->s_first_data_block;
667
0
  }
668
669
0
out:
670
0
  return err;
671
0
}
672
673
static errcode_t extent_fallocate(ext2_filsys fs, int flags, ext2_ino_t ino,
674
              struct ext2_inode *inode, blk64_t goal,
675
              blk64_t start, blk64_t len)
676
0
{
677
0
  ext2_extent_handle_t  handle;
678
0
  struct ext2fs_extent  left_extent, right_extent;
679
0
  struct ext2fs_extent  *left_adjacent, *right_adjacent;
680
0
  errcode_t   err;
681
0
  blk64_t     range_start, range_end = 0, end, next;
682
0
  blk64_t     count, goal_distance;
683
684
0
  end = start + len - 1;
685
0
  err = ext2fs_extent_open2(fs, ino, inode, &handle);
686
0
  if (err)
687
0
    return err;
688
689
  /*
690
   * Find the extent closest to the start of the alloc range.  We don't
691
   * check the return value because _goto() sets the current node to the
692
   * next-lowest extent if 'start' is in a hole; or the next-highest
693
   * extent if there aren't any lower ones; or doesn't set a current node
694
   * if there was a real error reading the extent tree.  In that case,
695
   * _get() will error out.
696
   */
697
0
start_again:
698
0
  ext2fs_extent_goto(handle, start);
699
0
  err = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &left_extent);
700
0
  if (err == EXT2_ET_NO_CURRENT_NODE) {
701
0
    blk64_t max_blocks = ext2fs_blocks_count(fs->super);
702
703
0
    if (goal == ~0ULL)
704
0
      goal = ext2fs_find_inode_goal(fs, ino, inode, start);
705
0
    err = ext2fs_find_first_zero_block_bitmap2(fs->block_map,
706
0
            goal, max_blocks - 1, &goal);
707
0
    goal += start;
708
0
    err = ext_falloc_helper(fs, flags, ino, inode, handle, NULL,
709
0
          NULL, start, len, goal);
710
0
    goto errout;
711
0
  } else if (err)
712
0
    goto errout;
713
714
0
  dbg_print_extent("ext_falloc initial", &left_extent);
715
0
  next = left_extent.e_lblk + left_extent.e_len;
716
0
  if (left_extent.e_lblk > start) {
717
    /* The nearest extent we found was beyond start??? */
718
0
    goal = left_extent.e_pblk - (left_extent.e_lblk - start);
719
0
    err = ext_falloc_helper(fs, flags, ino, inode, handle, NULL,
720
0
          &left_extent, start,
721
0
          min(len, left_extent.e_lblk - start),
722
0
          goal);
723
0
    if (err)
724
0
      goto errout;
725
726
0
    goto start_again;
727
0
  } else if (next >= start) {
728
0
    range_start = next;
729
0
    left_adjacent = &left_extent;
730
0
  } else {
731
0
    range_start = start;
732
0
    left_adjacent = NULL;
733
0
  }
734
0
  goal = left_extent.e_pblk + (range_start - left_extent.e_lblk);
735
736
0
  do {
737
0
    err = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_LEAF,
738
0
             &right_extent);
739
0
    dbg_printf("%s: ino=%d get next =%d\n", __func__, ino,
740
0
         (int)err);
741
0
    dbg_print_extent("ext_falloc next", &right_extent);
742
    /* Stop if we've seen this extent before */
743
0
    if (!err && right_extent.e_lblk <= left_extent.e_lblk)
744
0
      err = EXT2_ET_EXTENT_NO_NEXT;
745
746
0
    if (err && err != EXT2_ET_EXTENT_NO_NEXT)
747
0
      goto errout;
748
0
    if (err == EXT2_ET_EXTENT_NO_NEXT ||
749
0
        right_extent.e_lblk > end + 1) {
750
0
      range_end = end;
751
0
      right_adjacent = NULL;
752
0
    } else {
753
      /* Handle right_extent.e_lblk <= end */
754
0
      range_end = right_extent.e_lblk - 1;
755
0
      right_adjacent = &right_extent;
756
0
    }
757
0
    goal_distance = range_start - next;
758
0
    if (err != EXT2_ET_EXTENT_NO_NEXT &&
759
0
        goal_distance > (range_end - right_extent.e_lblk))
760
0
      goal = right_extent.e_pblk -
761
0
          (right_extent.e_lblk - range_start);
762
763
0
    dbg_printf("%s: ino=%d rstart=%llu rend=%llu\n", __func__, ino,
764
0
         range_start, range_end);
765
0
    err = 0;
766
0
    if (range_start <= range_end) {
767
0
      count = range_end - range_start + 1;
768
0
      err = ext_falloc_helper(fs, flags, ino, inode, handle,
769
0
            left_adjacent, right_adjacent,
770
0
            range_start, count, goal);
771
0
      if (err)
772
0
        goto errout;
773
0
    }
774
775
0
    if (range_end == end)
776
0
      break;
777
778
0
    err = ext2fs_extent_goto(handle, right_extent.e_lblk);
779
0
    if (err)
780
0
      goto errout;
781
0
    next = right_extent.e_lblk + right_extent.e_len;
782
0
    left_extent = right_extent;
783
0
    left_adjacent = &left_extent;
784
0
    range_start = next;
785
0
    goal = left_extent.e_pblk + (range_start - left_extent.e_lblk);
786
0
  } while (range_end < end);
787
788
0
errout:
789
0
  ext2fs_extent_free(handle);
790
0
  return err;
791
0
}
792
793
/*
794
 * Map physical blocks to a range of logical blocks within a file.  The range
795
 * of logical blocks are (start, start + len).  If there are already extents,
796
 * the mappings will try to extend the mappings; otherwise, it will try to map
797
 * start as if logical block 0 points to goal.  If goal is ~0ULL, then the goal
798
 * is calculated based on the inode group.
799
 *
800
 * Flags:
801
 * - EXT2_FALLOCATE_ZERO_BLOCKS: Zero the blocks that are allocated.
802
 * - EXT2_FALLOCATE_FORCE_INIT: Create only initialized extents.
803
 * - EXT2_FALLOCATE_FORCE_UNINIT: Create only uninitialized extents.
804
 * - EXT2_FALLOCATE_INIT_BEYOND_EOF: Create extents beyond EOF.
805
 *
806
 * If neither FORCE_INIT nor FORCE_UNINIT are specified, this function will
807
 * try to expand any extents it finds, zeroing blocks as necessary.
808
 */
809
errcode_t ext2fs_fallocate(ext2_filsys fs, int flags, ext2_ino_t ino,
810
         struct ext2_inode *inode, blk64_t goal,
811
         blk64_t start, blk64_t len)
812
0
{
813
0
  struct ext2_inode inode_buf;
814
0
  blk64_t     blk, x, zero_blk, last = 0;
815
0
  int     zero_len = 0;
816
0
  errcode_t   err;
817
818
0
  if (((flags & EXT2_FALLOCATE_FORCE_INIT) &&
819
0
      (flags & EXT2_FALLOCATE_FORCE_UNINIT)) ||
820
0
     (flags & ~EXT2_FALLOCATE_ALL_FLAGS))
821
0
    return EXT2_ET_INVALID_ARGUMENT;
822
823
0
  if (len > ext2fs_blocks_count(fs->super))
824
0
    return EXT2_ET_BLOCK_ALLOC_FAIL;
825
0
  else if (len == 0)
826
0
    return 0;
827
828
  /* Read inode structure if necessary */
829
0
  if (!inode) {
830
0
    err = ext2fs_read_inode(fs, ino, &inode_buf);
831
0
    if (err)
832
0
      return err;
833
0
    inode = &inode_buf;
834
0
  }
835
0
  dbg_printf("%s: ino=%d start=%llu len=%llu goal=%llu\n", __func__, ino,
836
0
       start, len, goal);
837
838
0
  if (inode->i_flags & EXT4_EXTENTS_FL) {
839
0
    err = extent_fallocate(fs, flags, ino, inode, goal, start, len);
840
0
    goto out;
841
0
  }
842
843
  /* XXX: Allocate a bunch of blocks the slow way */
844
0
  for (blk = start; blk < start + len; blk++) {
845
0
    err = ext2fs_bmap2(fs, ino, inode, NULL, 0, blk, 0, &x);
846
0
    if (err)
847
0
      return err;
848
0
    if (x)
849
0
      continue;
850
851
0
    err = ext2fs_bmap2(fs, ino, inode, NULL, BMAP_ALLOC,
852
0
           blk, 0, &x);
853
0
    if (err)
854
0
      goto errout;
855
0
    if ((zero_len && (x != last+1)) ||
856
0
        (zero_len >= 65536)) {
857
0
      err = ext2fs_zero_blocks2(fs, zero_blk, zero_len,
858
0
              NULL, NULL);
859
0
      zero_len = 0;
860
0
      if (err)
861
0
        goto errout;
862
0
    }
863
0
    if (zero_len == 0) {
864
0
      zero_blk = x;
865
0
      zero_len = 1;
866
0
    } else {
867
0
      zero_len++;
868
0
    }
869
0
    last = x;
870
0
  }
871
872
0
out:
873
0
  if (inode == &inode_buf)
874
0
    ext2fs_write_inode(fs, ino, inode);
875
0
errout:
876
0
  if (zero_len)
877
0
    ext2fs_zero_blocks2(fs, zero_blk, zero_len, NULL, NULL);
878
0
  return err;
879
0
}