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