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