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