Coverage Report

Created: 2026-03-07 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/e2fsprogs/lib/ext2fs/link.c
Line
Count
Source
1
/*
2
 * link.c --- create links in a ext2fs directory
3
 *
4
 * Copyright (C) 1993, 1994 Theodore Ts'o.
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
#include <stdio.h>
14
#include <string.h>
15
#if HAVE_UNISTD_H
16
#include <unistd.h>
17
#endif
18
19
#include "ext2_fs.h"
20
#include "ext2fs.h"
21
#include "ext2fsP.h"
22
23
0
#define EXT2_DX_ROOT_OFF 24
24
25
struct dx_frame {
26
  void *buf;
27
  blk64_t pblock;
28
  struct ext2_dx_countlimit *head;
29
  struct ext2_dx_entry *entries;
30
  struct ext2_dx_entry *at;
31
};
32
33
struct dx_lookup_info {
34
  const char *name;
35
  int namelen;
36
  int hash_alg;
37
  __u32 hash;
38
  unsigned levels;
39
  struct dx_frame frames[EXT4_HTREE_LEVEL];
40
};
41
42
static errcode_t alloc_dx_frame(ext2_filsys fs, struct dx_frame *frame)
43
0
{
44
0
  return ext2fs_get_mem(fs->blocksize, &frame->buf);
45
0
}
46
47
static void dx_release(struct dx_lookup_info *info)
48
0
{
49
0
  unsigned level;
50
51
0
  for (level = 0; level < info->levels; level++) {
52
0
    if (info->frames[level].buf == NULL)
53
0
      break;
54
0
    ext2fs_free_mem(&(info->frames[level].buf));
55
0
  }
56
0
  info->levels = 0;
57
0
}
58
59
static void dx_search_entry(struct dx_frame *frame, int count, __u32 hash)
60
0
{
61
0
  struct ext2_dx_entry *p, *q, *m;
62
63
0
  p = frame->entries + 1;
64
0
  q = frame->entries + count - 1;
65
0
  while (p <= q) {
66
0
    m = p + (q - p) / 2;
67
0
    if (ext2fs_le32_to_cpu(m->hash) > hash)
68
0
      q = m - 1;
69
0
    else
70
0
      p = m + 1;
71
0
  }
72
0
  frame->at = p - 1;
73
0
}
74
75
static errcode_t load_logical_dir_block(ext2_filsys fs, ext2_ino_t dir,
76
          struct ext2_inode *diri, blk64_t block,
77
          blk64_t *pblk, void *buf)
78
0
{
79
0
  errcode_t errcode;
80
0
  int ret_flags;
81
82
0
  errcode = ext2fs_bmap2(fs, dir, diri, NULL, 0, block, &ret_flags,
83
0
             pblk);
84
0
  if (errcode)
85
0
    return errcode;
86
0
  if (ret_flags & BMAP_RET_UNINIT)
87
0
    return EXT2_ET_DIR_CORRUPTED;
88
0
  return ext2fs_read_dir_block4(fs, *pblk, buf, 0, dir);
89
0
}
90
91
static errcode_t dx_lookup(ext2_filsys fs, ext2_ino_t dir,
92
         struct ext2_inode *diri, struct dx_lookup_info *info)
93
0
{
94
0
  struct ext2_dx_root_info *root;
95
0
  errcode_t errcode;
96
0
  int level = 0;
97
0
  int count, limit;
98
0
  int hash_alg;
99
0
  int hash_flags = diri->i_flags & EXT4_CASEFOLD_FL;
100
0
  __u32 minor_hash;
101
0
  struct dx_frame *frame;
102
103
0
  errcode = alloc_dx_frame(fs, &(info->frames[0]));
104
0
  if (errcode)
105
0
    return errcode;
106
0
  info->levels = 1;
107
108
0
  errcode = load_logical_dir_block(fs, dir, diri, 0,
109
0
           &(info->frames[0].pblock),
110
0
           info->frames[0].buf);
111
0
  if (errcode)
112
0
    goto out_err;
113
0
  root = (struct ext2_dx_root_info *) ((char *)info->frames[0].buf +
114
0
               EXT2_DX_ROOT_OFF);
115
0
  hash_alg = root->hash_version;
116
0
  if (hash_alg != EXT2_HASH_TEA && hash_alg != EXT2_HASH_HALF_MD4 &&
117
0
      hash_alg != EXT2_HASH_LEGACY) {
118
0
    errcode = EXT2_ET_DIRHASH_UNSUPP;
119
0
    goto out_err;
120
0
  }
121
0
  if (hash_alg <= EXT2_HASH_TEA &&
122
0
      fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH)
123
0
    hash_alg += 3;
124
0
  if (root->indirect_levels >= ext2_dir_htree_level(fs)) {
125
0
    errcode = EXT2_ET_DIR_CORRUPTED;
126
0
    goto out_err;
127
0
  }
128
0
  info->hash_alg = hash_alg;
129
130
0
  errcode = ext2fs_dirhash2(hash_alg, info->name, info->namelen,
131
0
          fs->encoding, hash_flags,
132
0
          fs->super->s_hash_seed, &info->hash,
133
0
          &minor_hash);
134
0
  if (errcode)
135
0
    goto out_err;
136
137
0
  for (level = 0; level <= root->indirect_levels; level++) {
138
0
    frame = &(info->frames[level]);
139
0
    if (level > 0) {
140
0
      errcode = alloc_dx_frame(fs, frame);
141
0
      if (errcode)
142
0
        goto out_err;
143
0
      info->levels++;
144
145
0
      errcode = load_logical_dir_block(fs, dir, diri,
146
0
        ext2fs_le32_to_cpu(info->frames[level-1].at->block) & 0x0fffffff,
147
0
        &(frame->pblock), frame->buf);
148
0
      if (errcode)
149
0
        goto out_err;
150
0
    }
151
0
    errcode = ext2fs_get_dx_countlimit(fs, frame->buf,
152
0
               &(frame->head), NULL);
153
0
    if (errcode)
154
0
      goto out_err;
155
0
    count = ext2fs_le16_to_cpu(frame->head->count);
156
0
    limit = ext2fs_le16_to_cpu(frame->head->limit);
157
0
    frame->entries = (struct ext2_dx_entry *)(frame->head);
158
0
    if (!count || count > limit) {
159
0
      errcode = EXT2_ET_DIR_CORRUPTED;
160
0
      goto out_err;
161
0
    }
162
163
0
    dx_search_entry(frame, count, info->hash);
164
0
  }
165
0
  return 0;
166
0
out_err:
167
0
  dx_release(info);
168
0
  return errcode;
169
0
}
170
171
struct link_struct  {
172
  ext2_filsys fs;
173
  const char  *name;
174
  int   namelen;
175
  ext2_ino_t  inode;
176
  int   flags;
177
  int   done;
178
  unsigned int  blocksize;
179
  errcode_t err;
180
  struct ext2_super_block *sb;
181
};
182
183
static int link_proc(ext2_ino_t dir EXT2FS_ATTR((unused)),
184
         int entru EXT2FS_ATTR((unused)),
185
         struct ext2_dir_entry *dirent,
186
         int  offset,
187
         int  blocksize,
188
         char *buf,
189
         void *priv_data)
190
0
{
191
0
  struct link_struct *ls = (struct link_struct *) priv_data;
192
0
  struct ext2_dir_entry *next;
193
0
  unsigned int rec_len, min_rec_len, curr_rec_len;
194
0
  int ret = 0;
195
0
  int csum_size = 0;
196
197
0
  if (ls->done)
198
0
    return DIRENT_ABORT;
199
200
0
  rec_len = EXT2_DIR_REC_LEN(ls->namelen);
201
202
0
  ls->err = ext2fs_get_rec_len(ls->fs, dirent, &curr_rec_len);
203
0
  if (ls->err)
204
0
    return DIRENT_ABORT;
205
206
0
  if (ext2fs_has_feature_metadata_csum(ls->fs->super))
207
0
    csum_size = sizeof(struct ext2_dir_entry_tail);
208
  /*
209
   * See if the following directory entry (if any) is unused;
210
   * if so, absorb it into this one.
211
   */
212
0
  next = (struct ext2_dir_entry *) (buf + offset + curr_rec_len);
213
0
  if ((offset + (int) curr_rec_len < blocksize - (8 + csum_size)) &&
214
0
      (next->inode == 0) &&
215
0
      (offset + (int) curr_rec_len + (int) next->rec_len <= blocksize)) {
216
0
    curr_rec_len += next->rec_len;
217
0
    ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
218
0
    if (ls->err)
219
0
      return DIRENT_ABORT;
220
0
    ret = DIRENT_CHANGED;
221
0
  }
222
223
  /*
224
   * If the directory entry is used, see if we can split the
225
   * directory entry to make room for the new name.  If so,
226
   * truncate it and return.
227
   */
228
0
  if (dirent->inode) {
229
0
    min_rec_len = EXT2_DIR_REC_LEN(ext2fs_dirent_name_len(dirent));
230
0
    if (curr_rec_len < (min_rec_len + rec_len))
231
0
      return ret;
232
0
    rec_len = curr_rec_len - min_rec_len;
233
0
    ls->err = ext2fs_set_rec_len(ls->fs, min_rec_len, dirent);
234
0
    if (ls->err)
235
0
      return DIRENT_ABORT;
236
0
    next = (struct ext2_dir_entry *) (buf + offset +
237
0
              dirent->rec_len);
238
0
    next->inode = 0;
239
0
    ext2fs_dirent_set_name_len(next, 0);
240
0
    ext2fs_dirent_set_file_type(next, 0);
241
0
    ls->err = ext2fs_set_rec_len(ls->fs, rec_len, next);
242
0
    if (ls->err)
243
0
      return DIRENT_ABORT;
244
0
    return DIRENT_CHANGED;
245
0
  }
246
247
  /*
248
   * If we get this far, then the directory entry is not used.
249
   * See if we can fit the request entry in.  If so, do it.
250
   */
251
0
  if (curr_rec_len < rec_len)
252
0
    return ret;
253
0
  dirent->inode = ls->inode;
254
0
  ext2fs_dirent_set_name_len(dirent, ls->namelen);
255
0
  strncpy(dirent->name, ls->name, ls->namelen);
256
0
  if (ext2fs_has_feature_filetype(ls->sb))
257
0
    ext2fs_dirent_set_file_type(dirent,
258
0
              ls->flags & EXT2FS_LINK_FT_MASK);
259
260
0
  ls->done++;
261
0
  return DIRENT_ABORT|DIRENT_CHANGED;
262
0
}
263
264
static errcode_t add_dirent_to_buf(ext2_filsys fs, e2_blkcnt_t blockcnt,
265
           char *buf, ext2_ino_t dir,
266
           struct ext2_inode *diri, const char *name,
267
           ext2_ino_t ino, int flags, blk64_t *pblkp)
268
0
{
269
0
  struct dir_context ctx;
270
0
  struct link_struct ls;
271
0
  errcode_t retval;
272
273
0
  retval = load_logical_dir_block(fs, dir, diri, blockcnt, pblkp, buf);
274
0
  if (retval)
275
0
    return retval;
276
0
  ctx.errcode = 0;
277
0
  ctx.func = link_proc;
278
0
  ctx.dir = dir;
279
0
  ctx.flags = DIRENT_FLAG_INCLUDE_EMPTY;
280
0
  ctx.buf = buf;
281
0
  ctx.priv_data = &ls;
282
283
0
  ls.fs = fs;
284
0
  ls.name = name;
285
0
  ls.namelen = strlen(name);
286
0
  ls.inode = ino;
287
0
  ls.flags = flags;
288
0
  ls.done = 0;
289
0
  ls.sb = fs->super;
290
0
  ls.blocksize = fs->blocksize;
291
0
  ls.err = 0;
292
293
0
  ext2fs_process_dir_block(fs, pblkp, blockcnt, 0, 0, &ctx);
294
0
  if (ctx.errcode)
295
0
    return ctx.errcode;
296
0
  if (ls.err)
297
0
    return ls.err;
298
0
  if (!ls.done)
299
0
    return EXT2_ET_DIR_NO_SPACE;
300
0
  return 0;
301
0
}
302
303
struct dx_hash_map {
304
  __u32 hash;
305
  int size;
306
  int off;
307
};
308
309
static EXT2_QSORT_TYPE dx_hash_map_cmp(const void *ap, const void *bp)
310
0
{
311
0
  const struct dx_hash_map *a = ap, *b = bp;
312
313
0
  if (a->hash < b->hash)
314
0
    return -1;
315
0
  if (a->hash > b->hash)
316
0
    return 1;
317
0
  return 0;
318
0
}
319
320
static errcode_t dx_move_dirents(ext2_filsys fs, struct dx_hash_map *map,
321
         int count, void *from, void *to)
322
0
{
323
0
  struct ext2_dir_entry *de;
324
0
  int i;
325
0
  int rec_len = 0;
326
0
  errcode_t retval;
327
0
  int csum_size = 0;
328
0
  void *base = to;
329
330
0
  if (ext2fs_has_feature_metadata_csum(fs->super))
331
0
    csum_size = sizeof(struct ext2_dir_entry_tail);
332
333
0
  for (i = 0; i < count; i++) {
334
0
    de = (struct ext2_dir_entry *) ((char *)from + map[i].off);
335
0
    rec_len = EXT2_DIR_REC_LEN(ext2fs_dirent_name_len(de));
336
0
    memcpy(to, de, rec_len);
337
0
    retval = ext2fs_set_rec_len(fs, rec_len, to);
338
0
    if (retval)
339
0
      return retval;
340
0
    to = (char *)to + rec_len;
341
0
  }
342
  /*
343
   * Update rec_len of the last dir entry to stretch to the end of block
344
   */
345
0
  to = (char *)to - rec_len;
346
0
  rec_len = fs->blocksize - ((char *)to - (char *)base) - csum_size;
347
0
  retval = ext2fs_set_rec_len(fs, rec_len, to);
348
0
  if (retval)
349
0
    return retval;
350
0
  if (csum_size)
351
0
    ext2fs_initialize_dirent_tail(fs,
352
0
        EXT2_DIRENT_TAIL(base, fs->blocksize));
353
0
  return 0;
354
0
}
355
356
static errcode_t dx_insert_entry(ext2_filsys fs, ext2_ino_t dir,
357
         struct dx_lookup_info *info, int level,
358
         __u32 hash, blk64_t lblk)
359
0
{
360
0
  int pcount;
361
0
  struct ext2_dx_entry *top, *new;
362
363
0
  pcount = ext2fs_le16_to_cpu(info->frames[level].head->count);
364
0
  top = info->frames[level].entries + pcount;
365
0
  new = info->frames[level].at + 1;
366
0
  memmove(new + 1, new, (char *)top - (char *)new);
367
0
  new->hash = ext2fs_cpu_to_le32(hash);
368
0
  new->block = ext2fs_cpu_to_le32(lblk);
369
0
  info->frames[level].head->count = ext2fs_cpu_to_le16(pcount + 1);
370
0
  return ext2fs_write_dir_block4(fs, info->frames[level].pblock,
371
0
               info->frames[level].buf, 0, dir);
372
0
}
373
374
static errcode_t dx_split_leaf(ext2_filsys fs, ext2_ino_t dir,
375
             struct ext2_inode *diri,
376
             struct dx_lookup_info *info, void *buf,
377
             blk64_t leaf_pblk, blk64_t new_lblk,
378
             blk64_t new_pblk)
379
0
{
380
0
  int hash_flags = diri->i_flags & EXT4_CASEFOLD_FL;
381
0
  struct ext2_dir_entry *de;
382
0
  void *buf2;
383
0
  errcode_t retval = 0;
384
0
  unsigned int rec_len;
385
0
  unsigned int offset, move_size;
386
0
  int i, count = 0;
387
0
  struct dx_hash_map *map;
388
0
  int continued;
389
0
  __u32 minor_hash;
390
391
0
  retval = ext2fs_get_mem(fs->blocksize, &buf2);
392
0
  if (retval)
393
0
    return retval;
394
0
  retval = ext2fs_get_array(fs->blocksize / 12,
395
0
          sizeof(struct dx_hash_map), &map);
396
0
  if (retval) {
397
0
    ext2fs_free_mem(&buf2);
398
0
    return retval;
399
0
  }
400
0
  for (offset = 0; offset < fs->blocksize; offset += rec_len) {
401
0
    de = (struct ext2_dir_entry *) ((char *)buf + offset);
402
0
    retval = ext2fs_get_rec_len(fs, de, &rec_len);
403
0
    if (retval)
404
0
      goto out;
405
0
    if (ext2fs_dirent_name_len(de) > 0 && de->inode) {
406
0
      map[count].off = offset;
407
0
      map[count].size = rec_len;
408
0
      retval = ext2fs_dirhash2(info->hash_alg, de->name,
409
0
          ext2fs_dirent_name_len(de),
410
0
          fs->encoding, hash_flags,
411
0
          fs->super->s_hash_seed,
412
0
          &(map[count].hash),
413
0
          &minor_hash);
414
0
      if (retval)
415
0
        goto out;
416
0
      count++;
417
0
    }
418
0
  }
419
0
  qsort(map, count, sizeof(struct dx_hash_map), dx_hash_map_cmp);
420
0
  move_size = 0;
421
  /* Find place to split block */
422
0
  for (i = count - 1; i >= 0; i--) {
423
0
    if (move_size + map[i].size / 2 > fs->blocksize / 2)
424
0
      break;
425
0
    move_size += map[i].size;
426
0
  }
427
  /* Let i be the first entry to move */
428
0
  i++;
429
  /* Move selected directory entries to new block */
430
0
  retval = dx_move_dirents(fs, map + i, count - i, buf, buf2);
431
0
  if (retval)
432
0
    goto out;
433
0
  retval = ext2fs_write_dir_block4(fs, new_pblk, buf2, 0, dir);
434
0
  if (retval)
435
0
    goto out;
436
  /* Repack remaining entries in the old block */
437
0
  retval = dx_move_dirents(fs, map, i, buf, buf2);
438
0
  if (retval)
439
0
    goto out;
440
0
  retval = ext2fs_write_dir_block4(fs, leaf_pblk, buf2, 0, dir);
441
0
  if (retval)
442
0
    goto out;
443
  /* Update parent node */
444
0
  continued = map[i].hash == map[i-1].hash;
445
0
  retval = dx_insert_entry(fs, dir, info, info->levels - 1,
446
0
         map[i].hash + continued, new_lblk);
447
0
out:
448
0
  ext2fs_free_mem(&buf2);
449
0
  ext2fs_free_mem(&map);
450
0
  return retval;
451
0
}
452
453
static errcode_t dx_grow_tree(ext2_filsys fs, ext2_ino_t dir,
454
            struct ext2_inode *diri,
455
            struct dx_lookup_info *info, void *buf,
456
            blk64_t leaf_pblk)
457
0
{
458
0
  int i;
459
0
  errcode_t retval;
460
0
  ext2_off64_t size = EXT2_I_SIZE(diri);
461
0
  blk64_t lblk, pblk;
462
0
  struct ext2_dir_entry *de;
463
0
  struct ext2_dx_countlimit *head;
464
0
  int csum_size = 0;
465
0
  int count;
466
467
0
  if (ext2fs_has_feature_metadata_csum(fs->super))
468
0
    csum_size = sizeof(struct ext2_dx_tail);
469
470
  /* Find level which can accommodate new child */
471
0
  for (i = info->levels - 1; i >= 0; i--)
472
0
    if (ext2fs_le16_to_cpu(info->frames[i].head->count) <
473
0
        ext2fs_le16_to_cpu(info->frames[i].head->limit))
474
0
      break;
475
  /* Need to grow tree depth? */
476
0
  if (i < 0 && info->levels >= ext2_dir_htree_level(fs))
477
0
    return EXT2_ET_DIR_NO_SPACE;
478
0
  lblk = size / fs->blocksize;
479
0
  size += fs->blocksize;
480
0
  retval = ext2fs_inode_size_set(fs, diri, size);
481
0
  if (retval)
482
0
    return retval;
483
0
  retval = ext2fs_fallocate(fs,
484
0
      EXT2_FALLOCATE_FORCE_INIT | EXT2_FALLOCATE_ZERO_BLOCKS,
485
0
      dir, diri, 0, lblk, 1);
486
0
  if (retval)
487
0
    return retval;
488
0
  retval = ext2fs_write_inode(fs, dir, diri);
489
0
  if (retval)
490
0
    return retval;
491
0
  retval = ext2fs_bmap2(fs, dir, diri, NULL, 0, lblk, NULL, &pblk);
492
0
  if (retval)
493
0
    return retval;
494
  /* Only leaf addition needed? */
495
0
  if (i == (int)info->levels - 1)
496
0
    return dx_split_leaf(fs, dir, diri, info, buf, leaf_pblk,
497
0
             lblk, pblk);
498
499
0
  de = buf;
500
0
  de->inode = 0;
501
0
  ext2fs_dirent_set_name_len(de, 0);
502
0
  ext2fs_dirent_set_file_type(de, 0);
503
0
  retval = ext2fs_set_rec_len(fs, fs->blocksize, de);
504
0
  if (retval)
505
0
    return retval;
506
0
  head = (struct ext2_dx_countlimit *) ((char *)buf + 8);
507
0
  count = ext2fs_le16_to_cpu(info->frames[i+1].head->count);
508
  /* Growing tree depth? */
509
0
  if (i < 0) {
510
0
    struct ext2_dx_root_info *root;
511
512
0
    memcpy(head, info->frames[0].entries,
513
0
           count * sizeof(struct ext2_dx_entry));
514
0
    head->limit = ext2fs_cpu_to_le16(
515
0
        (fs->blocksize - (8 + csum_size)) /
516
0
        sizeof(struct ext2_dx_entry));
517
    /* head->count gets set by memcpy above to correct value */
518
519
    /* Now update tree root */
520
0
    info->frames[0].head->count = ext2fs_cpu_to_le16(1);
521
0
    info->frames[0].entries[0].block = ext2fs_cpu_to_le32(lblk);
522
0
    root = (struct ext2_dx_root_info *)
523
0
      ((char *)info->frames[0].buf + EXT2_DX_ROOT_OFF);
524
0
    root->indirect_levels++;
525
0
  } else {
526
    /* Splitting internal node in two */
527
0
    int count1 = count / 2;
528
0
    int count2 = count - count1;
529
0
    __u32 split_hash = ext2fs_le32_to_cpu(info->frames[i+1].entries[count1].hash);
530
531
0
    memcpy(head, info->frames[i+1].entries + count1,
532
0
           count2 * sizeof(struct ext2_dx_entry));
533
0
    head->count = ext2fs_cpu_to_le16(count2);
534
0
    head->limit = ext2fs_cpu_to_le16(
535
0
        (fs->blocksize - (8 + csum_size)) /
536
0
        sizeof(struct ext2_dx_entry));
537
0
    info->frames[i+1].head->count = ext2fs_cpu_to_le16(count1);
538
539
    /* Update parent node */
540
0
    retval = dx_insert_entry(fs, dir, info, i, split_hash, lblk);
541
0
    if (retval)
542
0
      return retval;
543
544
0
  }
545
  /* Writeout split block / updated root */
546
0
  retval = ext2fs_write_dir_block4(fs, info->frames[i+1].pblock,
547
0
           info->frames[i+1].buf, 0, dir);
548
0
  if (retval)
549
0
    return retval;
550
  /* Writeout new tree block */
551
0
  retval = ext2fs_write_dir_block4(fs, pblk, buf, 0, dir);
552
0
  if (retval)
553
0
    return retval;
554
0
  return 0;
555
0
}
556
557
static errcode_t dx_link(ext2_filsys fs, ext2_ino_t dir,
558
       struct ext2_inode *diri, const char *name,
559
       ext2_ino_t ino, int flags)
560
0
{
561
0
  struct dx_lookup_info dx_info;
562
0
  errcode_t retval;
563
0
  void *blockbuf;
564
0
  unsigned restart = 0;
565
0
  blk64_t leaf_pblk;
566
567
0
  retval = ext2fs_get_mem(fs->blocksize, &blockbuf);
568
0
  if (retval)
569
0
    return retval;
570
571
0
  dx_info.name = name;
572
0
  dx_info.namelen = strlen(name);
573
0
again:
574
0
  retval = dx_lookup(fs, dir, diri, &dx_info);
575
0
  if (retval)
576
0
    goto free_buf;
577
578
0
  retval = add_dirent_to_buf(fs,
579
0
    ext2fs_le32_to_cpu(dx_info.frames[dx_info.levels-1].at->block) & 0x0fffffff,
580
0
    blockbuf, dir, diri, name, ino, flags, &leaf_pblk);
581
  /*
582
   * Success or error other than ENOSPC...? We are done. We may need upto
583
   * two tries to add entry. One to split htree node and another to add
584
   * new leaf block.
585
   */
586
0
  if (restart >= dx_info.levels || retval != EXT2_ET_DIR_NO_SPACE)
587
0
    goto free_frames;
588
0
  retval = dx_grow_tree(fs, dir, diri, &dx_info, blockbuf, leaf_pblk);
589
0
  if (retval)
590
0
    goto free_frames;
591
  /* Restart everything now that the tree is larger */
592
0
  restart++;
593
0
  dx_release(&dx_info);
594
0
  goto again;
595
0
free_frames:
596
0
  dx_release(&dx_info);
597
0
free_buf:
598
0
  ext2fs_free_mem(&blockbuf);
599
0
  return retval;
600
0
}
601
602
/*
603
 * Note: the low 3 bits of the flags field are used as the directory
604
 * entry filetype.
605
 */
606
#ifdef __TURBOC__
607
 #pragma argsused
608
#endif
609
errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name,
610
          ext2_ino_t ino, int flags)
611
0
{
612
0
  errcode_t   retval;
613
0
  struct link_struct  ls;
614
0
  struct ext2_inode inode;
615
616
0
  EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
617
618
0
  if (!(fs->flags & EXT2_FLAG_RW))
619
0
    return EXT2_ET_RO_FILSYS;
620
621
0
retry:
622
0
  if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0)
623
0
    return retval;
624
625
0
  if (inode.i_flags & EXT2_INDEX_FL)
626
0
    return dx_link(fs, dir, &inode, name, ino, flags);
627
628
0
  ls.fs = fs;
629
0
  ls.name = name;
630
0
  ls.namelen = name ? strlen(name) : 0;
631
0
  ls.inode = ino;
632
0
  ls.flags = flags;
633
0
  ls.done = 0;
634
0
  ls.sb = fs->super;
635
0
  ls.blocksize = fs->blocksize;
636
0
  ls.err = 0;
637
638
0
  if ((flags & EXT2FS_LINK_APPEND) &&
639
0
      !(inode.i_flags & EXT4_INLINE_DATA_FL)) {
640
0
    blk64_t   lblk, pblk = 0;
641
0
    struct dir_context ctx;
642
643
0
    lblk = (inode.i_size / fs->blocksize) - 1;
644
0
    retval = ext2fs_bmap2(fs, dir, &inode, NULL, 0, lblk,
645
0
              NULL, &pblk);
646
0
    if (retval)
647
0
      return retval;
648
649
0
    ctx.dir = dir;
650
0
    ctx.flags = DIRENT_FLAG_INCLUDE_EMPTY;
651
0
    ctx.func = link_proc;
652
0
    ctx.priv_data = &ls;
653
0
    ctx.errcode = 0;
654
0
    retval = ext2fs_get_mem(fs->blocksize, &ctx.buf);
655
0
    if (retval)
656
0
      return retval;
657
658
0
    ext2fs_process_dir_block(fs, &pblk, lblk, 0, 0, &ctx);
659
0
    retval = ctx.errcode;
660
0
    ext2fs_free_mem(&ctx.buf);
661
0
  } else {
662
0
    retval = ext2fs_dir_iterate2(fs, dir,
663
0
               DIRENT_FLAG_INCLUDE_EMPTY,
664
0
               NULL, link_proc, &ls);
665
0
  }
666
0
  if (retval)
667
0
    return retval;
668
0
  if (ls.err)
669
0
    return ls.err;
670
671
0
  if (!ls.done) {
672
0
    if (!(flags & EXT2FS_LINK_EXPAND))
673
0
      return EXT2_ET_DIR_NO_SPACE;
674
0
    retval = ext2fs_expand_dir(fs, dir);
675
0
    if (retval)
676
0
      return retval;
677
0
    goto retry;
678
0
  }
679
0
  return 0;
680
0
}