Whamcloud - gitweb
create_inode: don't indiscriminately #include "nls-enable.h"
[tools/e2fsprogs.git] / misc / create_inode.c
1 /*
2  * create_inode.c --- create an inode
3  *
4  * Copyright (C) 2014 Robert Yang <liezhi.yang@windriver.com>
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 <time.h>
13 #include <unistd.h>
14 #include <limits.h> /* for PATH_MAX */
15 #ifdef HAVE_ATTR_XATTR_H
16 #include <attr/xattr.h>
17 #endif
18
19 #include "create_inode.h"
20 #include "nls-enable.h"
21
22 #if __STDC_VERSION__ < 199901L
23 # if __GNUC__ >= 2
24 #  define __func__ __FUNCTION__
25 # else
26 #  define __func__ "<unknown>"
27 # endif
28 #endif
29
30 /* 64KiB is the minimium blksize to best minimize system call overhead. */
31 #ifndef IO_BUFSIZE
32 #define IO_BUFSIZE 64*1024
33 #endif
34
35 /* Block size for `st_blocks' */
36 #ifndef S_BLKSIZE
37 #define S_BLKSIZE 512
38 #endif
39
40 /* Link an inode number to a directory */
41 static errcode_t add_link(ext2_filsys fs, ext2_ino_t parent_ino,
42                           ext2_ino_t ino, const char *name)
43 {
44         struct ext2_inode       inode;
45         errcode_t               retval;
46
47         retval = ext2fs_read_inode(fs, ino, &inode);
48         if (retval) {
49                 com_err(__func__, retval, "while reading inode %u", ino);
50                 return retval;
51         }
52
53         retval = ext2fs_link(fs, parent_ino, name, ino, inode.i_flags);
54         if (retval == EXT2_ET_DIR_NO_SPACE) {
55                 retval = ext2fs_expand_dir(fs, parent_ino);
56                 if (retval) {
57                         com_err(__func__, retval, "while expanding directory");
58                         return retval;
59                 }
60                 retval = ext2fs_link(fs, parent_ino, name, ino, inode.i_flags);
61         }
62         if (retval) {
63                 com_err(__func__, retval, "while linking %s", name);
64                 return retval;
65         }
66
67         inode.i_links_count++;
68
69         retval = ext2fs_write_inode(fs, ino, &inode);
70         if (retval)
71                 com_err(__func__, retval, "while writing inode %u", ino);
72
73         return retval;
74 }
75
76 /* Fill the uid, gid, mode and time for the inode */
77 static void fill_inode(struct ext2_inode *inode, struct stat *st)
78 {
79         if (st != NULL) {
80                 inode->i_uid = st->st_uid;
81                 inode->i_gid = st->st_gid;
82                 inode->i_mode |= st->st_mode;
83                 inode->i_atime = st->st_atime;
84                 inode->i_mtime = st->st_mtime;
85                 inode->i_ctime = st->st_ctime;
86         }
87 }
88
89 /* Set the uid, gid, mode and time for the inode */
90 static errcode_t set_inode_extra(ext2_filsys fs, ext2_ino_t cwd,
91                                  ext2_ino_t ino, struct stat *st)
92 {
93         errcode_t               retval;
94         struct ext2_inode       inode;
95
96         retval = ext2fs_read_inode(fs, ino, &inode);
97         if (retval) {
98                 com_err(__func__, retval, "while reading inode %u", ino);
99                 return retval;
100         }
101
102         fill_inode(&inode, st);
103
104         retval = ext2fs_write_inode(fs, ino, &inode);
105         if (retval)
106                 com_err(__func__, retval, "while writing inode %u", ino);
107         return retval;
108 }
109
110 static errcode_t set_inode_xattr(ext2_filsys fs, ext2_ino_t ino, const char *filename)
111 {
112 #ifdef HAVE_LLISTXATTR
113         errcode_t                       retval, close_retval;
114         struct ext2_inode               inode;
115         struct ext2_xattr_handle        *handle;
116         ssize_t                         size, value_size;
117         char                            *list;
118         int                             i;
119
120         size = llistxattr(filename, NULL, 0);
121         if (size == -1) {
122                 com_err(__func__, errno, "llistxattr failed on %s", filename);
123                 return errno;
124         } else if (size == 0) {
125                 return 0;
126         }
127
128         retval = ext2fs_xattrs_open(fs, ino, &handle);
129         if (retval) {
130                 if (retval == EXT2_ET_MISSING_EA_FEATURE)
131                         return 0;
132                 com_err(__func__, retval, "while opening inode %u", ino);
133                 return retval;
134         }
135
136         retval = ext2fs_get_mem(size, &list);
137         if (retval) {
138                 com_err(__func__, retval, "whilst allocating memory");
139                 goto out;
140         }
141
142         size = llistxattr(filename, list, size);
143         if (size == -1) {
144                 com_err(__func__, errno, "llistxattr failed on %s", filename);
145                 retval = errno;
146                 goto out;
147         }
148
149         for (i = 0; i < size; i += strlen(&list[i]) + 1) {
150                 const char *name = &list[i];
151                 char *value;
152
153                 value_size = getxattr(filename, name, NULL, 0);
154                 if (value_size == -1) {
155                         com_err(__func__, errno, "getxattr failed on %s",
156                                 filename);
157                         retval = errno;
158                         break;
159                 }
160
161                 retval = ext2fs_get_mem(value_size, &value);
162                 if (retval) {
163                         com_err(__func__, retval, "whilst allocating memory");
164                         break;
165                 }
166
167                 value_size = getxattr(filename, name, value, value_size);
168                 if (value_size == -1) {
169                         ext2fs_free_mem(&value);
170                         com_err(__func__, errno, "getxattr failed on %s",
171                                 filename);
172                         retval = errno;
173                         break;
174                 }
175
176                 retval = ext2fs_xattr_set(handle, name, value, value_size);
177                 ext2fs_free_mem(&value);
178                 if (retval) {
179                         com_err(__func__, retval,
180                                 "while writing xattr %u", ino);
181                         break;
182                 }
183
184         }
185  out:
186         ext2fs_free_mem(&list);
187         close_retval = ext2fs_xattrs_close(&handle);
188         if (close_retval) {
189                 com_err(__func__, retval, "while closing inode %u", ino);
190                 retval = retval ? retval : close_retval;
191         }
192         return retval;
193 #else /* HAVE_LLISTXATTR */
194         return 0;
195 #endif  /* HAVE_LLISTXATTR */
196 }
197
198 /* Make a special files (block and character devices), fifo's, and sockets  */
199 errcode_t do_mknod_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
200                             struct stat *st)
201 {
202         ext2_ino_t              ino;
203         errcode_t               retval;
204         struct ext2_inode       inode;
205         unsigned long           devmajor, devminor, mode;
206         int                     filetype;
207
208         switch(st->st_mode & S_IFMT) {
209         case S_IFCHR:
210                 mode = LINUX_S_IFCHR;
211                 filetype = EXT2_FT_CHRDEV;
212                 break;
213         case S_IFBLK:
214                 mode = LINUX_S_IFBLK;
215                 filetype =  EXT2_FT_BLKDEV;
216                 break;
217         case S_IFIFO:
218                 mode = LINUX_S_IFIFO;
219                 filetype = EXT2_FT_FIFO;
220                 break;
221         case S_IFSOCK:
222                 mode = LINUX_S_IFSOCK;
223                 filetype = EXT2_FT_SOCK;
224                 break;
225         default:
226                 return EXT2_ET_INVALID_ARGUMENT;
227         }
228
229         if (!(fs->flags & EXT2_FLAG_RW)) {
230                 com_err(__func__, 0, "Filesystem opened read/only");
231                 return EROFS;
232         }
233         retval = ext2fs_new_inode(fs, cwd, 010755, 0, &ino);
234         if (retval) {
235                 com_err(__func__, retval, 0);
236                 return retval;
237         }
238
239 #ifdef DEBUGFS
240         printf("Allocated inode: %u\n", ino);
241 #endif
242         retval = ext2fs_link(fs, cwd, name, ino, filetype);
243         if (retval == EXT2_ET_DIR_NO_SPACE) {
244                 retval = ext2fs_expand_dir(fs, cwd);
245                 if (retval) {
246                         com_err(__func__, retval, "while expanding directory");
247                         return retval;
248                 }
249                 retval = ext2fs_link(fs, cwd, name, ino, filetype);
250         }
251         if (retval) {
252                 com_err(name, retval, 0);
253                 return retval;
254         }
255         if (ext2fs_test_inode_bitmap2(fs->inode_map, ino))
256                 com_err(__func__, 0, "Warning: inode already set");
257         ext2fs_inode_alloc_stats2(fs, ino, +1, 0);
258         memset(&inode, 0, sizeof(inode));
259         inode.i_mode = mode;
260         inode.i_atime = inode.i_ctime = inode.i_mtime =
261                 fs->now ? fs->now : time(0);
262
263         if (filetype != S_IFIFO) {
264                 devmajor = major(st->st_rdev);
265                 devminor = minor(st->st_rdev);
266
267                 if ((devmajor < 256) && (devminor < 256)) {
268                         inode.i_block[0] = devmajor * 256 + devminor;
269                         inode.i_block[1] = 0;
270                 } else {
271                         inode.i_block[0] = 0;
272                         inode.i_block[1] = (devminor & 0xff) | (devmajor << 8) |
273                                            ((devminor & ~0xff) << 12);
274                 }
275         }
276         inode.i_links_count = 1;
277
278         retval = ext2fs_write_new_inode(fs, ino, &inode);
279         if (retval)
280                 com_err(__func__, retval, "while creating inode %u", ino);
281
282         return retval;
283 }
284
285 /* Make a symlink name -> target */
286 errcode_t do_symlink_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
287                               char *target, ext2_ino_t root)
288 {
289         char                    *cp;
290         ext2_ino_t              parent_ino;
291         errcode_t               retval;
292
293         cp = strrchr(name, '/');
294         if (cp) {
295                 *cp = 0;
296                 retval = ext2fs_namei(fs, root, cwd, name, &parent_ino);
297                 if (retval) {
298                         com_err(name, retval, 0);
299                         return retval;
300                 }
301                 name = cp+1;
302         } else
303                 parent_ino = cwd;
304
305 try_again:
306         retval = ext2fs_symlink(fs, parent_ino, 0, name, target);
307         if (retval == EXT2_ET_DIR_NO_SPACE) {
308                 retval = ext2fs_expand_dir(fs, parent_ino);
309                 if (retval) {
310                         com_err("do_symlink_internal", retval,
311                                 "while expanding directory");
312                         return retval;
313                 }
314                 goto try_again;
315         }
316         if (retval)
317                 com_err("ext2fs_symlink", retval, 0);
318         return retval;
319 }
320
321 /* Make a directory in the fs */
322 errcode_t do_mkdir_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
323                             struct stat *st, ext2_ino_t root)
324 {
325         char                    *cp;
326         ext2_ino_t              parent_ino;
327         errcode_t               retval;
328
329
330         cp = strrchr(name, '/');
331         if (cp) {
332                 *cp = 0;
333                 retval = ext2fs_namei(fs, root, cwd, name, &parent_ino);
334                 if (retval) {
335                         com_err(name, retval, 0);
336                         return retval;
337                 }
338                 name = cp+1;
339         } else
340                 parent_ino = cwd;
341
342 try_again:
343         retval = ext2fs_mkdir(fs, parent_ino, 0, name);
344         if (retval == EXT2_ET_DIR_NO_SPACE) {
345                 retval = ext2fs_expand_dir(fs, parent_ino);
346                 if (retval) {
347                         com_err(__func__, retval, "while expanding directory");
348                         return retval;
349                 }
350                 goto try_again;
351         }
352         if (retval)
353                 com_err("ext2fs_mkdir", retval, 0);
354         return retval;
355 }
356
357 static errcode_t copy_file(ext2_filsys fs, int fd, ext2_ino_t newfile,
358                            int bufsize, int make_holes)
359 {
360         ext2_file_t     e2_file;
361         errcode_t       retval, close_ret;
362         int             got;
363         unsigned int    written;
364         char            *buf;
365         char            *ptr;
366         char            *zero_buf;
367         int             cmp;
368
369         retval = ext2fs_file_open(fs, newfile,
370                                   EXT2_FILE_WRITE, &e2_file);
371         if (retval)
372                 return retval;
373
374         retval = ext2fs_get_mem(bufsize, &buf);
375         if (retval) {
376                 com_err("copy_file", retval, "can't allocate buffer\n");
377                 goto out_close;
378         }
379
380         /* This is used for checking whether the whole block is zero */
381         retval = ext2fs_get_memzero(bufsize, &zero_buf);
382         if (retval) {
383                 com_err("copy_file", retval, "can't allocate zero buffer\n");
384                 goto out_free_buf;
385         }
386
387         while (1) {
388                 got = read(fd, buf, bufsize);
389                 if (got == 0)
390                         break;
391                 if (got < 0) {
392                         retval = errno;
393                         goto fail;
394                 }
395                 ptr = buf;
396
397                 /* Sparse copy */
398                 if (make_holes) {
399                         /* Check whether all is zero */
400                         cmp = memcmp(ptr, zero_buf, got);
401                         if (cmp == 0) {
402                                  /* The whole block is zero, make a hole */
403                                 retval = ext2fs_file_lseek(e2_file, got,
404                                                            EXT2_SEEK_CUR,
405                                                            NULL);
406                                 if (retval)
407                                         goto fail;
408                                 got = 0;
409                         }
410                 }
411
412                 /* Normal copy */
413                 while (got > 0) {
414                         retval = ext2fs_file_write(e2_file, ptr,
415                                                    got, &written);
416                         if (retval)
417                                 goto fail;
418
419                         got -= written;
420                         ptr += written;
421                 }
422         }
423
424 fail:
425         ext2fs_free_mem(&zero_buf);
426 out_free_buf:
427         ext2fs_free_mem(&buf);
428 out_close:
429         close_ret = ext2fs_file_close(e2_file);
430         if (retval == 0)
431                 retval = close_ret;
432         return retval;
433 }
434
435 static int is_hardlink(struct hdlinks_s *hdlinks, dev_t dev, ino_t ino)
436 {
437         int i;
438
439         for (i = 0; i < hdlinks->count; i++) {
440                 if (hdlinks->hdl[i].src_dev == dev &&
441                     hdlinks->hdl[i].src_ino == ino)
442                         return i;
443         }
444         return -1;
445 }
446
447 /* Copy the native file to the fs */
448 errcode_t do_write_internal(ext2_filsys fs, ext2_ino_t cwd, const char *src,
449                             const char *dest, ext2_ino_t root)
450 {
451         int             fd;
452         struct stat     statbuf;
453         ext2_ino_t      newfile;
454         errcode_t       retval;
455         struct ext2_inode inode;
456         int             bufsize = IO_BUFSIZE;
457         int             make_holes = 0;
458
459         fd = ext2fs_open_file(src, O_RDONLY, 0);
460         if (fd < 0) {
461                 com_err(src, errno, 0);
462                 return errno;
463         }
464         if (fstat(fd, &statbuf) < 0) {
465                 com_err(src, errno, 0);
466                 close(fd);
467                 return errno;
468         }
469
470         retval = ext2fs_namei(fs, root, cwd, dest, &newfile);
471         if (retval == 0) {
472                 close(fd);
473                 return EXT2_ET_FILE_EXISTS;
474         }
475
476         retval = ext2fs_new_inode(fs, cwd, 010755, 0, &newfile);
477         if (retval) {
478                 com_err(__func__, retval, 0);
479                 close(fd);
480                 return retval;
481         }
482 #ifdef DEBUGFS
483         printf("Allocated inode: %u\n", newfile);
484 #endif
485         retval = ext2fs_link(fs, cwd, dest, newfile,
486                                 EXT2_FT_REG_FILE);
487         if (retval == EXT2_ET_DIR_NO_SPACE) {
488                 retval = ext2fs_expand_dir(fs, cwd);
489                 if (retval) {
490                         com_err(__func__, retval, "while expanding directory");
491                         close(fd);
492                         return retval;
493                 }
494                 retval = ext2fs_link(fs, cwd, dest, newfile,
495                                         EXT2_FT_REG_FILE);
496         }
497         if (retval) {
498                 com_err(dest, retval, 0);
499                 close(fd);
500                 return errno;
501         }
502         if (ext2fs_test_inode_bitmap2(fs->inode_map, newfile))
503                 com_err(__func__, 0, "Warning: inode already set");
504         ext2fs_inode_alloc_stats2(fs, newfile, +1, 0);
505         memset(&inode, 0, sizeof(inode));
506         inode.i_mode = (statbuf.st_mode & ~LINUX_S_IFMT) | LINUX_S_IFREG;
507         inode.i_atime = inode.i_ctime = inode.i_mtime =
508                 fs->now ? fs->now : time(0);
509         inode.i_links_count = 1;
510         retval = ext2fs_inode_size_set(fs, &inode, statbuf.st_size);
511         if (retval) {
512                 com_err(dest, retval, 0);
513                 close(fd);
514                 return retval;
515         }
516         if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
517                                       EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
518                 inode.i_flags |= EXT4_INLINE_DATA_FL;
519         } else if (fs->super->s_feature_incompat &
520                    EXT3_FEATURE_INCOMPAT_EXTENTS) {
521                 int i;
522                 struct ext3_extent_header *eh;
523
524                 eh = (struct ext3_extent_header *) &inode.i_block[0];
525                 eh->eh_depth = 0;
526                 eh->eh_entries = 0;
527                 eh->eh_magic = ext2fs_cpu_to_le16(EXT3_EXT_MAGIC);
528                 i = (sizeof(inode.i_block) - sizeof(*eh)) /
529                         sizeof(struct ext3_extent);
530                 eh->eh_max = ext2fs_cpu_to_le16(i);
531                 inode.i_flags |= EXT4_EXTENTS_FL;
532         }
533
534         retval = ext2fs_write_new_inode(fs, newfile, &inode);
535         if (retval) {
536                 com_err(__func__, retval, "while creating inode %u", newfile);
537                 close(fd);
538                 return retval;
539         }
540         if (inode.i_flags & EXT4_INLINE_DATA_FL) {
541                 retval = ext2fs_inline_data_init(fs, newfile);
542                 if (retval) {
543                         com_err("copy_file", retval, 0);
544                         close(fd);
545                         return retval;
546                 }
547         }
548         if (LINUX_S_ISREG(inode.i_mode)) {
549                 if (statbuf.st_blocks < statbuf.st_size / S_BLKSIZE) {
550                         make_holes = 1;
551                         /*
552                          * Use I/O blocksize as buffer size when
553                          * copying sparse files.
554                          */
555                         bufsize = statbuf.st_blksize;
556                 }
557                 retval = copy_file(fs, fd, newfile, bufsize, make_holes);
558                 if (retval)
559                         com_err("copy_file", retval, 0);
560         }
561         close(fd);
562
563         return retval;
564 }
565
566 /* Copy files from source_dir to fs */
567 static errcode_t __populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
568                                const char *source_dir, ext2_ino_t root,
569                                struct hdlinks_s *hdlinks)
570 {
571         const char      *name;
572         DIR             *dh;
573         struct dirent   *dent;
574         struct stat     st;
575         char            ln_target[PATH_MAX];
576         unsigned int    save_inode;
577         ext2_ino_t      ino;
578         errcode_t       retval = 0;
579         int             read_cnt;
580         int             hdlink;
581
582         if (chdir(source_dir) < 0) {
583                 com_err(__func__, errno,
584                         _("while changing working directory to \"%s\""),
585                         source_dir);
586                 return errno;
587         }
588
589         if (!(dh = opendir("."))) {
590                 com_err(__func__, errno,
591                         _("while opening directory \"%s\""), source_dir);
592                 return errno;
593         }
594
595         while ((dent = readdir(dh))) {
596                 if ((!strcmp(dent->d_name, ".")) ||
597                     (!strcmp(dent->d_name, "..")))
598                         continue;
599                 if (lstat(dent->d_name, &st)) {
600                         com_err(__func__, errno, _("while lstat \"%s\""),
601                                 dent->d_name);
602                         goto out;
603                 }
604                 name = dent->d_name;
605
606                 /* Check for hardlinks */
607                 save_inode = 0;
608                 if (!S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode) &&
609                     st.st_nlink > 1) {
610                         hdlink = is_hardlink(hdlinks, st.st_dev, st.st_ino);
611                         if (hdlink >= 0) {
612                                 retval = add_link(fs, parent_ino,
613                                                   hdlinks->hdl[hdlink].dst_ino,
614                                                   name);
615                                 if (retval) {
616                                         com_err(__func__, retval,
617                                                 "while linking %s", name);
618                                         goto out;
619                                 }
620                                 continue;
621                         } else
622                                 save_inode = 1;
623                 }
624
625                 switch(st.st_mode & S_IFMT) {
626                 case S_IFCHR:
627                 case S_IFBLK:
628                 case S_IFIFO:
629                 case S_IFSOCK:
630                         retval = do_mknod_internal(fs, parent_ino, name, &st);
631                         if (retval) {
632                                 com_err(__func__, retval,
633                                         _("while creating special file "
634                                           "\"%s\""), name);
635                                 goto out;
636                         }
637                         break;
638                 case S_IFLNK:
639                         read_cnt = readlink(name, ln_target,
640                                             sizeof(ln_target) - 1);
641                         if (read_cnt == -1) {
642                                 com_err(__func__, errno,
643                                         _("while trying to readlink \"%s\""),
644                                         name);
645                                 retval = errno;
646                                 goto out;
647                         }
648                         ln_target[read_cnt] = '\0';
649                         retval = do_symlink_internal(fs, parent_ino, name,
650                                                      ln_target, root);
651                         if (retval) {
652                                 com_err(__func__, retval,
653                                         _("while writing symlink\"%s\""),
654                                         name);
655                                 goto out;
656                         }
657                         break;
658                 case S_IFREG:
659                         retval = do_write_internal(fs, parent_ino, name, name,
660                                                    root);
661                         if (retval) {
662                                 com_err(__func__, retval,
663                                         _("while writing file \"%s\""), name);
664                                 goto out;
665                         }
666                         break;
667                 case S_IFDIR:
668                         retval = do_mkdir_internal(fs, parent_ino, name, &st,
669                                                    root);
670                         if (retval) {
671                                 com_err(__func__, retval,
672                                         _("while making dir \"%s\""), name);
673                                 goto out;
674                         }
675                         retval = ext2fs_namei(fs, root, parent_ino,
676                                               name, &ino);
677                         if (retval) {
678                                 com_err(name, retval, 0);
679                                         goto out;
680                         }
681                         /* Populate the dir recursively*/
682                         retval = __populate_fs(fs, ino, name, root, hdlinks);
683                         if (retval) {
684                                 com_err(__func__, retval,
685                                         _("while adding dir \"%s\""), name);
686                                 goto out;
687                         }
688                         if (chdir("..")) {
689                                 com_err(__func__, errno, _("during cd .."));
690                                 retval = errno;
691                                 goto out;
692                         }
693                         break;
694                 default:
695                         com_err(__func__, 0,
696                                 _("ignoring entry \"%s\""), name);
697                 }
698
699                 retval =  ext2fs_namei(fs, root, parent_ino, name, &ino);
700                 if (retval) {
701                         com_err(name, retval, 0);
702                         goto out;
703                 }
704
705                 retval = set_inode_extra(fs, parent_ino, ino, &st);
706                 if (retval) {
707                         com_err(__func__, retval,
708                                 _("while setting inode for \"%s\""), name);
709                         goto out;
710                 }
711
712                 retval = set_inode_xattr(fs, ino, name);
713                 if (retval) {
714                         com_err(__func__, retval,
715                                 _("while setting xattrs for \"%s\""), name);
716                         goto out;
717                 }
718
719                 /* Save the hardlink ino */
720                 if (save_inode) {
721                         /*
722                          * Check whether need more memory, and we don't need
723                          * free() since the lifespan will be over after the fs
724                          * populated.
725                          */
726                         if (hdlinks->count == hdlinks->size) {
727                                 void *p = realloc(hdlinks->hdl,
728                                                 (hdlinks->size + HDLINK_CNT) *
729                                                 sizeof(struct hdlink_s));
730                                 if (p == NULL) {
731                                         com_err(name, errno,
732                                                 _("Not enough memory"));
733                                         retval = EXT2_ET_NO_MEMORY;
734                                         goto out;
735                                 }
736                                 hdlinks->hdl = p;
737                                 hdlinks->size += HDLINK_CNT;
738                         }
739                         hdlinks->hdl[hdlinks->count].src_dev = st.st_dev;
740                         hdlinks->hdl[hdlinks->count].src_ino = st.st_ino;
741                         hdlinks->hdl[hdlinks->count].dst_ino = ino;
742                         hdlinks->count++;
743                 }
744         }
745
746 out:
747         closedir(dh);
748         return retval;
749 }
750
751 errcode_t populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
752                       const char *source_dir, ext2_ino_t root)
753 {
754         struct hdlinks_s hdlinks;
755         errcode_t retval;
756
757         hdlinks.count = 0;
758         hdlinks.size = HDLINK_CNT;
759         hdlinks.hdl = realloc(NULL, hdlinks.size * sizeof(struct hdlink_s));
760         if (hdlinks.hdl == NULL) {
761                 com_err(__func__, errno, "Not enough memory");
762                 return errno;
763         }
764
765         retval = __populate_fs(fs, parent_ino, source_dir, root, &hdlinks);
766
767         free(hdlinks.hdl);
768         return retval;
769 }