Whamcloud - gitweb
Fix compilation warnings about functions declared in sys/xattr.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 #define _FILE_OFFSET_BITS       64
13 #define _LARGEFILE64_SOURCE     1
14 #define _GNU_SOURCE             1
15
16 #include "config.h"
17 #include <time.h>
18 #include <sys/types.h>
19 #include <unistd.h>
20 #include <limits.h> /* for PATH_MAX */
21 #if defined HAVE_SYS_XATTR_H
22 #include <sys/xattr.h>
23 #elif defined HAVE_ATTR_XATTR_H
24 #include <attr/xattr.h>
25 #endif
26 #include <sys/ioctl.h>
27 #ifdef HAVE_SYS_SYSMACROS_H
28 #include <sys/sysmacros.h>
29 #endif
30
31 #include <ext2fs/ext2fs.h>
32 #include <ext2fs/ext2_types.h>
33 #include <ext2fs/fiemap.h>
34
35 #include "create_inode.h"
36 #include "support/nls-enable.h"
37
38 /* 64KiB is the minimum blksize to best minimize system call overhead. */
39 #define COPY_FILE_BUFLEN        65536
40
41 static int ext2_file_type(unsigned int mode)
42 {
43         if (LINUX_S_ISREG(mode))
44                 return EXT2_FT_REG_FILE;
45
46         if (LINUX_S_ISDIR(mode))
47                 return EXT2_FT_DIR;
48
49         if (LINUX_S_ISCHR(mode))
50                 return EXT2_FT_CHRDEV;
51
52         if (LINUX_S_ISBLK(mode))
53                 return EXT2_FT_BLKDEV;
54
55         if (LINUX_S_ISLNK(mode))
56                 return EXT2_FT_SYMLINK;
57
58         if (LINUX_S_ISFIFO(mode))
59                 return EXT2_FT_FIFO;
60
61         if (LINUX_S_ISSOCK(mode))
62                 return EXT2_FT_SOCK;
63
64         return 0;
65 }
66
67 /* Link an inode number to a directory */
68 static errcode_t add_link(ext2_filsys fs, ext2_ino_t parent_ino,
69                           ext2_ino_t ino, const char *name)
70 {
71         struct ext2_inode       inode;
72         errcode_t               retval;
73
74         retval = ext2fs_read_inode(fs, ino, &inode);
75         if (retval) {
76                 com_err(__func__, retval, _("while reading inode %u"), ino);
77                 return retval;
78         }
79
80         retval = ext2fs_link(fs, parent_ino, name, ino,
81                              ext2_file_type(inode.i_mode));
82         if (retval == EXT2_ET_DIR_NO_SPACE) {
83                 retval = ext2fs_expand_dir(fs, parent_ino);
84                 if (retval) {
85                         com_err(__func__, retval,
86                                 _("while expanding directory"));
87                         return retval;
88                 }
89                 retval = ext2fs_link(fs, parent_ino, name, ino,
90                                      ext2_file_type(inode.i_mode));
91         }
92         if (retval) {
93                 com_err(__func__, retval, _("while linking \"%s\""), name);
94                 return retval;
95         }
96
97         inode.i_links_count++;
98
99         retval = ext2fs_write_inode(fs, ino, &inode);
100         if (retval)
101                 com_err(__func__, retval, _("while writing inode %u"), ino);
102
103         return retval;
104 }
105
106 /* Set the uid, gid, mode and time for the inode */
107 static errcode_t set_inode_extra(ext2_filsys fs, ext2_ino_t ino,
108                                  struct stat *st)
109 {
110         errcode_t               retval;
111         struct ext2_inode       inode;
112
113         retval = ext2fs_read_inode(fs, ino, &inode);
114         if (retval) {
115                 com_err(__func__, retval, _("while reading inode %u"), ino);
116                 return retval;
117         }
118
119         inode.i_uid = st->st_uid;
120         inode.i_gid = st->st_gid;
121         inode.i_mode |= st->st_mode;
122         inode.i_atime = st->st_atime;
123         inode.i_mtime = st->st_mtime;
124         inode.i_ctime = st->st_ctime;
125
126         retval = ext2fs_write_inode(fs, ino, &inode);
127         if (retval)
128                 com_err(__func__, retval, _("while writing inode %u"), ino);
129         return retval;
130 }
131
132 #ifdef HAVE_LLISTXATTR
133 static errcode_t set_inode_xattr(ext2_filsys fs, ext2_ino_t ino,
134                                  const char *filename)
135 {
136         errcode_t                       retval, close_retval;
137         struct ext2_xattr_handle        *handle;
138         ssize_t                         size, value_size;
139         char                            *list = NULL;
140         int                             i;
141
142         size = llistxattr(filename, NULL, 0);
143         if (size == -1) {
144                 retval = errno;
145                 com_err(__func__, retval, _("while listing attributes of \"%s\""),
146                         filename);
147                 return retval;
148         } else if (size == 0) {
149                 return 0;
150         }
151
152         retval = ext2fs_xattrs_open(fs, ino, &handle);
153         if (retval) {
154                 if (retval == EXT2_ET_MISSING_EA_FEATURE)
155                         return 0;
156                 com_err(__func__, retval, _("while opening inode %u"), ino);
157                 return retval;
158         }
159
160         retval = ext2fs_get_mem(size, &list);
161         if (retval) {
162                 com_err(__func__, retval, _("while allocating memory"));
163                 goto out;
164         }
165
166         size = llistxattr(filename, list, size);
167         if (size == -1) {
168                 retval = errno;
169                 com_err(__func__, retval, _("while listing attributes of \"%s\""),
170                         filename);
171                 goto out;
172         }
173
174         for (i = 0; i < size; i += strlen(&list[i]) + 1) {
175                 const char *name = &list[i];
176                 char *value;
177
178                 value_size = lgetxattr(filename, name, NULL, 0);
179                 if (value_size == -1) {
180                         retval = errno;
181                         com_err(__func__, retval,
182                                 _("while reading attribute \"%s\" of \"%s\""),
183                                 name, filename);
184                         break;
185                 }
186
187                 retval = ext2fs_get_mem(value_size, &value);
188                 if (retval) {
189                         com_err(__func__, retval, _("while allocating memory"));
190                         break;
191                 }
192
193                 value_size = lgetxattr(filename, name, value, value_size);
194                 if (value_size == -1) {
195                         ext2fs_free_mem(&value);
196                         retval = errno;
197                         com_err(__func__, retval,
198                                 _("while reading attribute \"%s\" of \"%s\""),
199                                 name, filename);
200                         break;
201                 }
202
203                 retval = ext2fs_xattr_set(handle, name, value, value_size);
204                 ext2fs_free_mem(&value);
205                 if (retval) {
206                         com_err(__func__, retval,
207                                 _("while writing attribute \"%s\" to inode %u"),
208                                 name, 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         return 0;
222 }
223 #else /* HAVE_LLISTXATTR */
224 static errcode_t set_inode_xattr(ext2_filsys fs EXT2FS_ATTR((unused)),
225                                  ext2_ino_t ino EXT2FS_ATTR((unused)),
226                                  const char *filename EXT2FS_ATTR((unused)))
227 {
228         return 0;
229 }
230 #endif  /* HAVE_LLISTXATTR */
231
232 /* Make a special files (block and character devices), fifo's, and sockets  */
233 errcode_t do_mknod_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
234                             struct stat *st)
235 {
236         ext2_ino_t              ino;
237         errcode_t               retval;
238         struct ext2_inode       inode;
239         unsigned long           devmajor, devminor, mode;
240         int                     filetype;
241
242         switch(st->st_mode & S_IFMT) {
243         case S_IFCHR:
244                 mode = LINUX_S_IFCHR;
245                 filetype = EXT2_FT_CHRDEV;
246                 break;
247         case S_IFBLK:
248                 mode = LINUX_S_IFBLK;
249                 filetype =  EXT2_FT_BLKDEV;
250                 break;
251         case S_IFIFO:
252                 mode = LINUX_S_IFIFO;
253                 filetype = EXT2_FT_FIFO;
254                 break;
255         case S_IFSOCK:
256                 mode = LINUX_S_IFSOCK;
257                 filetype = EXT2_FT_SOCK;
258                 break;
259         default:
260                 return EXT2_ET_INVALID_ARGUMENT;
261         }
262
263         retval = ext2fs_new_inode(fs, cwd, 010755, 0, &ino);
264         if (retval) {
265                 com_err(__func__, retval, _("while allocating inode \"%s\""),
266                         name);
267                 return retval;
268         }
269
270 #ifdef DEBUGFS
271         printf("Allocated inode: %u\n", ino);
272 #endif
273         retval = ext2fs_link(fs, cwd, name, ino, filetype);
274         if (retval == EXT2_ET_DIR_NO_SPACE) {
275                 retval = ext2fs_expand_dir(fs, cwd);
276                 if (retval) {
277                         com_err(__func__, retval,
278                                 _("while expanding directory"));
279                         return retval;
280                 }
281                 retval = ext2fs_link(fs, cwd, name, ino, filetype);
282         }
283         if (retval) {
284                 com_err(name, retval, _("while creating inode \"%s\""), name);
285                 return retval;
286         }
287         if (ext2fs_test_inode_bitmap2(fs->inode_map, ino))
288                 com_err(__func__, 0, "Warning: inode already set");
289         ext2fs_inode_alloc_stats2(fs, ino, +1, 0);
290         memset(&inode, 0, sizeof(inode));
291         inode.i_mode = mode;
292         inode.i_atime = inode.i_ctime = inode.i_mtime =
293                 fs->now ? fs->now : time(0);
294
295         if (filetype != S_IFIFO) {
296                 devmajor = major(st->st_rdev);
297                 devminor = minor(st->st_rdev);
298
299                 if ((devmajor < 256) && (devminor < 256)) {
300                         inode.i_block[0] = devmajor * 256 + devminor;
301                         inode.i_block[1] = 0;
302                 } else {
303                         inode.i_block[0] = 0;
304                         inode.i_block[1] = (devminor & 0xff) | (devmajor << 8) |
305                                            ((devminor & ~0xff) << 12);
306                 }
307         }
308         inode.i_links_count = 1;
309
310         retval = ext2fs_write_new_inode(fs, ino, &inode);
311         if (retval)
312                 com_err(__func__, retval, _("while writing inode %u"), ino);
313
314         return retval;
315 }
316
317 /* Make a symlink name -> target */
318 errcode_t do_symlink_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
319                               char *target, ext2_ino_t root)
320 {
321         char                    *cp;
322         ext2_ino_t              parent_ino;
323         errcode_t               retval;
324
325         cp = strrchr(name, '/');
326         if (cp) {
327                 *cp = 0;
328                 retval = ext2fs_namei(fs, root, cwd, name, &parent_ino);
329                 if (retval) {
330                         com_err(name, retval, 0);
331                         return retval;
332                 }
333                 name = cp+1;
334         } else
335                 parent_ino = cwd;
336
337         retval = ext2fs_symlink(fs, parent_ino, 0, name, target);
338         if (retval == EXT2_ET_DIR_NO_SPACE) {
339                 retval = ext2fs_expand_dir(fs, parent_ino);
340                 if (retval) {
341                         com_err("do_symlink_internal", retval,
342                                 _("while expanding directory"));
343                         return retval;
344                 }
345                 retval = ext2fs_symlink(fs, parent_ino, 0, name, target);
346         }
347         if (retval)
348                 com_err("ext2fs_symlink", retval,
349                         _("while creating symlink \"%s\""), name);
350         return retval;
351 }
352
353 /* Make a directory in the fs */
354 errcode_t do_mkdir_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
355                             ext2_ino_t root)
356 {
357         char                    *cp;
358         ext2_ino_t              parent_ino;
359         errcode_t               retval;
360
361
362         cp = strrchr(name, '/');
363         if (cp) {
364                 *cp = 0;
365                 retval = ext2fs_namei(fs, root, cwd, name, &parent_ino);
366                 if (retval) {
367                         com_err(name, retval, _("while looking up \"%s\""),
368                                 name);
369                         return retval;
370                 }
371                 name = cp+1;
372         } else
373                 parent_ino = cwd;
374
375         retval = ext2fs_mkdir(fs, parent_ino, 0, name);
376         if (retval == EXT2_ET_DIR_NO_SPACE) {
377                 retval = ext2fs_expand_dir(fs, parent_ino);
378                 if (retval) {
379                         com_err(__func__, retval,
380                                 _("while expanding directory"));
381                         return retval;
382                 }
383                 retval = ext2fs_mkdir(fs, parent_ino, 0, name);
384         }
385         if (retval)
386                 com_err("ext2fs_mkdir", retval,
387                         _("while creating directory \"%s\""), name);
388         return retval;
389 }
390
391 #if !defined HAVE_PREAD64 && !defined HAVE_PREAD
392 static ssize_t my_pread(int fd, void *buf, size_t count, off_t offset)
393 {
394         if (lseek(fd, offset, SEEK_SET) < 0)
395                 return 0;
396
397         return read(fd, buf, count);
398 }
399 #endif /* !defined HAVE_PREAD64 && !defined HAVE_PREAD */
400
401 static errcode_t copy_file_range(ext2_filsys fs, int fd, ext2_file_t e2_file,
402                                  off_t start, off_t end, char *buf,
403                                  char *zerobuf)
404 {
405         off_t off, bpos;
406         ssize_t got, blen;
407         unsigned int written;
408         char *ptr;
409         errcode_t err = 0;
410
411         for (off = start; off < end; off += COPY_FILE_BUFLEN) {
412 #ifdef HAVE_PREAD64
413                 got = pread64(fd, buf, COPY_FILE_BUFLEN, off);
414 #elif HAVE_PREAD
415                 got = pread(fd, buf, COPY_FILE_BUFLEN, off);
416 #else
417                 got = my_pread(fd, buf, COPY_FILE_BUFLEN, off);
418 #endif
419                 if (got < 0) {
420                         err = errno;
421                         goto fail;
422                 }
423                 for (bpos = 0, ptr = buf; bpos < got; bpos += fs->blocksize) {
424                         blen = fs->blocksize;
425                         if (blen > got - bpos)
426                                 blen = got - bpos;
427                         if (memcmp(ptr, zerobuf, blen) == 0) {
428                                 ptr += blen;
429                                 continue;
430                         }
431                         err = ext2fs_file_lseek(e2_file, off + bpos,
432                                                 EXT2_SEEK_SET, NULL);
433                         if (err)
434                                 goto fail;
435                         while (blen > 0) {
436                                 err = ext2fs_file_write(e2_file, ptr, blen,
437                                                         &written);
438                                 if (err)
439                                         goto fail;
440                                 if (written == 0) {
441                                         err = EIO;
442                                         goto fail;
443                                 }
444                                 blen -= written;
445                                 ptr += written;
446                         }
447                 }
448         }
449 fail:
450         return err;
451 }
452
453 #if defined(SEEK_DATA) && defined(SEEK_HOLE)
454 static errcode_t try_lseek_copy(ext2_filsys fs, int fd, struct stat *statbuf,
455                                 ext2_file_t e2_file, char *buf, char *zerobuf)
456 {
457         off_t data = 0, hole;
458         off_t data_blk, hole_blk;
459         errcode_t err = 0;
460
461         /* Try to use SEEK_DATA and SEEK_HOLE */
462         while (data < statbuf->st_size) {
463                 data = lseek(fd, data, SEEK_DATA);
464                 if (data < 0) {
465                         if (errno == ENXIO)
466                                 break;
467                         return EXT2_ET_UNIMPLEMENTED;
468                 }
469                 hole = lseek(fd, data, SEEK_HOLE);
470                 if (hole < 0)
471                         return EXT2_ET_UNIMPLEMENTED;
472
473                 data_blk = data & ~(fs->blocksize - 1);
474                 hole_blk = (hole + (fs->blocksize - 1)) & ~(fs->blocksize - 1);
475                 err = copy_file_range(fs, fd, e2_file, data_blk, hole_blk, buf,
476                                       zerobuf);
477                 if (err)
478                         return err;
479
480                 data = hole;
481         }
482
483         return err;
484 }
485 #endif /* SEEK_DATA and SEEK_HOLE */
486
487 #if defined(FS_IOC_FIEMAP)
488 static errcode_t try_fiemap_copy(ext2_filsys fs, int fd, ext2_file_t e2_file,
489                                  char *buf, char *zerobuf)
490 {
491 #define EXTENT_MAX_COUNT 512
492         struct fiemap *fiemap_buf;
493         struct fiemap_extent *ext_buf, *ext;
494         int ext_buf_size, fie_buf_size;
495         off_t pos = 0;
496         unsigned int i;
497         errcode_t err;
498
499         ext_buf_size = EXTENT_MAX_COUNT * sizeof(struct fiemap_extent);
500         fie_buf_size = sizeof(struct fiemap) + ext_buf_size;
501
502         err = ext2fs_get_memzero(fie_buf_size, &fiemap_buf);
503         if (err)
504                 return err;
505
506         ext_buf = fiemap_buf->fm_extents;
507         memset(fiemap_buf, 0, fie_buf_size);
508         fiemap_buf->fm_length = FIEMAP_MAX_OFFSET;
509         fiemap_buf->fm_flags |= FIEMAP_FLAG_SYNC;
510         fiemap_buf->fm_extent_count = EXTENT_MAX_COUNT;
511
512         do {
513                 fiemap_buf->fm_start = pos;
514                 memset(ext_buf, 0, ext_buf_size);
515                 err = ioctl(fd, FS_IOC_FIEMAP, fiemap_buf);
516                 if (err < 0 && (errno == EOPNOTSUPP || errno == ENOTTY)) {
517                         err = EXT2_ET_UNIMPLEMENTED;
518                         goto out;
519                 } else if (err < 0) {
520                         err = errno;
521                         goto out;
522                 } else if (fiemap_buf->fm_mapped_extents == 0)
523                         goto out;
524                 for (i = 0, ext = ext_buf; i < fiemap_buf->fm_mapped_extents;
525                      i++, ext++) {
526                         err = copy_file_range(fs, fd, e2_file, ext->fe_logical,
527                                               ext->fe_logical + ext->fe_length,
528                                               buf, zerobuf);
529                         if (err)
530                                 goto out;
531                 }
532
533                 ext--;
534                 /* Record file's logical offset this time */
535                 pos = ext->fe_logical + ext->fe_length;
536                 /*
537                  * If fm_extents array has been filled and
538                  * there are extents left, continue to cycle.
539                  */
540         } while (fiemap_buf->fm_mapped_extents == EXTENT_MAX_COUNT &&
541                  !(ext->fe_flags & FIEMAP_EXTENT_LAST));
542 out:
543         ext2fs_free_mem(&fiemap_buf);
544         return err;
545 }
546 #endif /* FS_IOC_FIEMAP */
547
548 static errcode_t copy_file(ext2_filsys fs, int fd, struct stat *statbuf,
549                            ext2_ino_t ino)
550 {
551         ext2_file_t e2_file;
552         char *buf = NULL, *zerobuf = NULL;
553         errcode_t err, close_err;
554
555         err = ext2fs_file_open(fs, ino, EXT2_FILE_WRITE, &e2_file);
556         if (err)
557                 return err;
558
559         err = ext2fs_get_mem(COPY_FILE_BUFLEN, &buf);
560         if (err)
561                 goto out;
562
563         err = ext2fs_get_memzero(fs->blocksize, &zerobuf);
564         if (err)
565                 goto out;
566
567 #if defined(SEEK_DATA) && defined(SEEK_HOLE)
568         err = try_lseek_copy(fs, fd, statbuf, e2_file, buf, zerobuf);
569         if (err != EXT2_ET_UNIMPLEMENTED)
570                 goto out;
571 #endif
572
573 #if defined(FS_IOC_FIEMAP)
574         err = try_fiemap_copy(fs, fd, e2_file, buf, zerobuf);
575         if (err != EXT2_ET_UNIMPLEMENTED)
576                 goto out;
577 #endif
578
579         err = copy_file_range(fs, fd, e2_file, 0, statbuf->st_size, buf,
580                               zerobuf);
581 out:
582         ext2fs_free_mem(&zerobuf);
583         ext2fs_free_mem(&buf);
584         close_err = ext2fs_file_close(e2_file);
585         if (err == 0)
586                 err = close_err;
587         return err;
588 }
589
590 static int is_hardlink(struct hdlinks_s *hdlinks, dev_t dev, ino_t ino)
591 {
592         int i;
593
594         for (i = 0; i < hdlinks->count; i++) {
595                 if (hdlinks->hdl[i].src_dev == dev &&
596                     hdlinks->hdl[i].src_ino == ino)
597                         return i;
598         }
599         return -1;
600 }
601
602 /* Copy the native file to the fs */
603 errcode_t do_write_internal(ext2_filsys fs, ext2_ino_t cwd, const char *src,
604                             const char *dest, ext2_ino_t root)
605 {
606         int             fd;
607         struct stat     statbuf;
608         ext2_ino_t      newfile;
609         errcode_t       retval;
610         struct ext2_inode inode;
611
612         fd = ext2fs_open_file(src, O_RDONLY, 0);
613         if (fd < 0) {
614                 retval = errno;
615                 com_err(__func__, retval, _("while opening \"%s\" to copy"),
616                         src);
617                 return retval;
618         }
619         if (fstat(fd, &statbuf) < 0) {
620                 retval = errno;
621                 goto out;
622         }
623
624         retval = ext2fs_namei(fs, root, cwd, dest, &newfile);
625         if (retval == 0) {
626                 retval = EXT2_ET_FILE_EXISTS;
627                 goto out;
628         }
629
630         retval = ext2fs_new_inode(fs, cwd, 010755, 0, &newfile);
631         if (retval)
632                 goto out;
633 #ifdef DEBUGFS
634         printf("Allocated inode: %u\n", newfile);
635 #endif
636         retval = ext2fs_link(fs, cwd, dest, newfile,
637                                 EXT2_FT_REG_FILE);
638         if (retval == EXT2_ET_DIR_NO_SPACE) {
639                 retval = ext2fs_expand_dir(fs, cwd);
640                 if (retval)
641                         goto out;
642                 retval = ext2fs_link(fs, cwd, dest, newfile,
643                                         EXT2_FT_REG_FILE);
644         }
645         if (retval)
646                 goto out;
647         if (ext2fs_test_inode_bitmap2(fs->inode_map, newfile))
648                 com_err(__func__, 0, "Warning: inode already set");
649         ext2fs_inode_alloc_stats2(fs, newfile, +1, 0);
650         memset(&inode, 0, sizeof(inode));
651         inode.i_mode = (statbuf.st_mode & ~LINUX_S_IFMT) | LINUX_S_IFREG;
652         inode.i_atime = inode.i_ctime = inode.i_mtime =
653                 fs->now ? fs->now : time(0);
654         inode.i_links_count = 1;
655         retval = ext2fs_inode_size_set(fs, &inode, statbuf.st_size);
656         if (retval)
657                 goto out;
658         if (ext2fs_has_feature_inline_data(fs->super)) {
659                 inode.i_flags |= EXT4_INLINE_DATA_FL;
660         } else if (ext2fs_has_feature_extents(fs->super)) {
661                 ext2_extent_handle_t handle;
662
663                 inode.i_flags &= ~EXT4_EXTENTS_FL;
664                 retval = ext2fs_extent_open2(fs, newfile, &inode, &handle);
665                 if (retval)
666                         goto out;
667                 ext2fs_extent_free(handle);
668         }
669
670         retval = ext2fs_write_new_inode(fs, newfile, &inode);
671         if (retval)
672                 goto out;
673         if (inode.i_flags & EXT4_INLINE_DATA_FL) {
674                 retval = ext2fs_inline_data_init(fs, newfile);
675                 if (retval)
676                         goto out;
677         }
678         if (LINUX_S_ISREG(inode.i_mode)) {
679                 retval = copy_file(fs, fd, &statbuf, newfile);
680                 if (retval)
681                         goto out;
682         }
683 out:
684         close(fd);
685         return retval;
686 }
687
688 /* Copy files from source_dir to fs */
689 static errcode_t __populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
690                                const char *source_dir, ext2_ino_t root,
691                                struct hdlinks_s *hdlinks)
692 {
693         const char      *name;
694         DIR             *dh;
695         struct dirent   *dent;
696         struct stat     st;
697         char            *ln_target = NULL;
698         unsigned int    save_inode;
699         ext2_ino_t      ino;
700         errcode_t       retval = 0;
701         int             read_cnt;
702         int             hdlink;
703
704         if (chdir(source_dir) < 0) {
705                 retval = errno;
706                 com_err(__func__, retval,
707                         _("while changing working directory to \"%s\""),
708                         source_dir);
709                 return retval;
710         }
711
712         if (!(dh = opendir("."))) {
713                 retval = errno;
714                 com_err(__func__, retval,
715                         _("while opening directory \"%s\""), source_dir);
716                 return retval;
717         }
718
719         while ((dent = readdir(dh))) {
720                 if ((!strcmp(dent->d_name, ".")) ||
721                     (!strcmp(dent->d_name, "..")))
722                         continue;
723                 if (lstat(dent->d_name, &st)) {
724                         retval = errno;
725                         com_err(__func__, retval, _("while lstat \"%s\""),
726                                 dent->d_name);
727                         goto out;
728                 }
729                 name = dent->d_name;
730
731                 /* Check for hardlinks */
732                 save_inode = 0;
733                 if (!S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode) &&
734                     st.st_nlink > 1) {
735                         hdlink = is_hardlink(hdlinks, st.st_dev, st.st_ino);
736                         if (hdlink >= 0) {
737                                 retval = add_link(fs, parent_ino,
738                                                   hdlinks->hdl[hdlink].dst_ino,
739                                                   name);
740                                 if (retval) {
741                                         com_err(__func__, retval,
742                                                 "while linking %s", name);
743                                         goto out;
744                                 }
745                                 continue;
746                         } else
747                                 save_inode = 1;
748                 }
749
750                 switch(st.st_mode & S_IFMT) {
751                 case S_IFCHR:
752                 case S_IFBLK:
753                 case S_IFIFO:
754                 case S_IFSOCK:
755                         retval = do_mknod_internal(fs, parent_ino, name, &st);
756                         if (retval) {
757                                 com_err(__func__, retval,
758                                         _("while creating special file "
759                                           "\"%s\""), name);
760                                 goto out;
761                         }
762                         break;
763                 case S_IFLNK:
764                         ln_target = malloc(st.st_size + 1);
765                         if (ln_target == NULL) {
766                                 com_err(__func__, retval,
767                                         _("malloc failed"));
768                                 goto out;
769                         }
770                         read_cnt = readlink(name, ln_target,
771                                             st.st_size + 1);
772                         if (read_cnt == -1) {
773                                 retval = errno;
774                                 com_err(__func__, retval,
775                                         _("while trying to read link \"%s\""),
776                                         name);
777                                 free(ln_target);
778                                 goto out;
779                         }
780                         if (read_cnt > st.st_size) {
781                                 com_err(__func__, retval,
782                                         _("symlink increased in size "
783                                           "between lstat() and readlink()"));
784                                 free(ln_target);
785                                 goto out;
786                         }
787                         ln_target[read_cnt] = '\0';
788                         retval = do_symlink_internal(fs, parent_ino, name,
789                                                      ln_target, root);
790                         free(ln_target);
791                         if (retval) {
792                                 com_err(__func__, retval,
793                                         _("while writing symlink\"%s\""),
794                                         name);
795                                 goto out;
796                         }
797                         break;
798                 case S_IFREG:
799                         retval = do_write_internal(fs, parent_ino, name, name,
800                                                    root);
801                         if (retval) {
802                                 com_err(__func__, retval,
803                                         _("while writing file \"%s\""), name);
804                                 goto out;
805                         }
806                         break;
807                 case S_IFDIR:
808                         /* Don't choke on /lost+found */
809                         if (parent_ino == EXT2_ROOT_INO &&
810                             strcmp(name, "lost+found") == 0)
811                                 goto find_lnf;
812                         retval = do_mkdir_internal(fs, parent_ino, name,
813                                                    root);
814                         if (retval) {
815                                 com_err(__func__, retval,
816                                         _("while making dir \"%s\""), name);
817                                 goto out;
818                         }
819 find_lnf:
820                         retval = ext2fs_namei(fs, root, parent_ino,
821                                               name, &ino);
822                         if (retval) {
823                                 com_err(name, retval, 0);
824                                         goto out;
825                         }
826                         /* Populate the dir recursively*/
827                         retval = __populate_fs(fs, ino, name, root, hdlinks);
828                         if (retval)
829                                 goto out;
830                         if (chdir("..")) {
831                                 retval = errno;
832                                 com_err(__func__, retval,
833                                         _("while changing directory"));
834                                 goto out;
835                         }
836                         break;
837                 default:
838                         com_err(__func__, 0,
839                                 _("ignoring entry \"%s\""), name);
840                 }
841
842                 retval =  ext2fs_namei(fs, root, parent_ino, name, &ino);
843                 if (retval) {
844                         com_err(name, retval, _("while looking up \"%s\""),
845                                 name);
846                         goto out;
847                 }
848
849                 retval = set_inode_extra(fs, ino, &st);
850                 if (retval) {
851                         com_err(__func__, retval,
852                                 _("while setting inode for \"%s\""), name);
853                         goto out;
854                 }
855
856                 retval = set_inode_xattr(fs, ino, name);
857                 if (retval) {
858                         com_err(__func__, retval,
859                                 _("while setting xattrs for \"%s\""), name);
860                         goto out;
861                 }
862
863                 /* Save the hardlink ino */
864                 if (save_inode) {
865                         /*
866                          * Check whether need more memory, and we don't need
867                          * free() since the lifespan will be over after the fs
868                          * populated.
869                          */
870                         if (hdlinks->count == hdlinks->size) {
871                                 void *p = realloc(hdlinks->hdl,
872                                                 (hdlinks->size + HDLINK_CNT) *
873                                                 sizeof(struct hdlink_s));
874                                 if (p == NULL) {
875                                         retval = EXT2_ET_NO_MEMORY;
876                                         com_err(name, retval,
877                                                 _("while saving inode data"));
878                                         goto out;
879                                 }
880                                 hdlinks->hdl = p;
881                                 hdlinks->size += HDLINK_CNT;
882                         }
883                         hdlinks->hdl[hdlinks->count].src_dev = st.st_dev;
884                         hdlinks->hdl[hdlinks->count].src_ino = st.st_ino;
885                         hdlinks->hdl[hdlinks->count].dst_ino = ino;
886                         hdlinks->count++;
887                 }
888         }
889
890 out:
891         closedir(dh);
892         return retval;
893 }
894
895 errcode_t populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
896                       const char *source_dir, ext2_ino_t root)
897 {
898         struct hdlinks_s hdlinks;
899         errcode_t retval;
900
901         if (!(fs->flags & EXT2_FLAG_RW)) {
902                 com_err(__func__, 0, "Filesystem opened readonly");
903                 return EROFS;
904         }
905
906         hdlinks.count = 0;
907         hdlinks.size = HDLINK_CNT;
908         hdlinks.hdl = realloc(NULL, hdlinks.size * sizeof(struct hdlink_s));
909         if (hdlinks.hdl == NULL) {
910                 retval = errno;
911                 com_err(__func__, retval, _("while allocating memory"));
912                 return retval;
913         }
914
915         retval = __populate_fs(fs, parent_ino, source_dir, root, &hdlinks);
916
917         free(hdlinks.hdl);
918         return retval;
919 }