Whamcloud - gitweb
Fix miscellaneous compiler warnings using "make gcc-wall"
[tools/e2fsprogs.git] / lib / ext2fs / link.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }