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