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