Whamcloud - gitweb
AOSP: Fix file offset overflow issue when file's size > 4G
[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)) &
485                             ~(off_t)(fs->blocksize - 1));
486                 err = copy_file_chunk(fs, fd, e2_file, data_blk, hole_blk, buf,
487                                       zerobuf);
488                 if (err)
489                         return err;
490
491                 data = hole;
492         }
493
494         return err;
495 }
496 #endif /* SEEK_DATA and SEEK_HOLE */
497
498 #if defined(FS_IOC_FIEMAP)
499 static errcode_t try_fiemap_copy(ext2_filsys fs, int fd, ext2_file_t e2_file,
500                                  char *buf, char *zerobuf)
501 {
502 #define EXTENT_MAX_COUNT 512
503         struct fiemap *fiemap_buf;
504         struct fiemap_extent *ext_buf, *ext;
505         int ext_buf_size, fie_buf_size;
506         off_t pos = 0;
507         unsigned int i;
508         errcode_t err;
509
510         ext_buf_size = EXTENT_MAX_COUNT * sizeof(struct fiemap_extent);
511         fie_buf_size = sizeof(struct fiemap) + ext_buf_size;
512
513         err = ext2fs_get_memzero(fie_buf_size, &fiemap_buf);
514         if (err)
515                 return err;
516
517         ext_buf = fiemap_buf->fm_extents;
518         memset(fiemap_buf, 0, fie_buf_size);
519         fiemap_buf->fm_length = FIEMAP_MAX_OFFSET;
520         fiemap_buf->fm_flags |= FIEMAP_FLAG_SYNC;
521         fiemap_buf->fm_extent_count = EXTENT_MAX_COUNT;
522
523         do {
524                 fiemap_buf->fm_start = pos;
525                 memset(ext_buf, 0, ext_buf_size);
526                 err = ioctl(fd, FS_IOC_FIEMAP, fiemap_buf);
527                 if (err < 0 && (errno == EOPNOTSUPP || errno == ENOTTY)) {
528                         err = EXT2_ET_UNIMPLEMENTED;
529                         goto out;
530                 } else if (err < 0) {
531                         err = errno;
532                         goto out;
533                 } else if (fiemap_buf->fm_mapped_extents == 0)
534                         goto out;
535                 for (i = 0, ext = ext_buf; i < fiemap_buf->fm_mapped_extents;
536                      i++, ext++) {
537                         err = copy_file_chunk(fs, fd, e2_file, ext->fe_logical,
538                                               ext->fe_logical + ext->fe_length,
539                                               buf, zerobuf);
540                         if (err)
541                                 goto out;
542                 }
543
544                 ext--;
545                 /* Record file's logical offset this time */
546                 pos = ext->fe_logical + ext->fe_length;
547                 /*
548                  * If fm_extents array has been filled and
549                  * there are extents left, continue to cycle.
550                  */
551         } while (fiemap_buf->fm_mapped_extents == EXTENT_MAX_COUNT &&
552                  !(ext->fe_flags & FIEMAP_EXTENT_LAST));
553 out:
554         ext2fs_free_mem(&fiemap_buf);
555         return err;
556 }
557 #endif /* FS_IOC_FIEMAP */
558
559 static errcode_t copy_file(ext2_filsys fs, int fd, struct stat *statbuf,
560                            ext2_ino_t ino)
561 {
562         ext2_file_t e2_file;
563         char *buf = NULL, *zerobuf = NULL;
564         errcode_t err, close_err;
565
566         err = ext2fs_file_open(fs, ino, EXT2_FILE_WRITE, &e2_file);
567         if (err)
568                 return err;
569
570         err = ext2fs_get_mem(COPY_FILE_BUFLEN, &buf);
571         if (err)
572                 goto out;
573
574         err = ext2fs_get_memzero(fs->blocksize, &zerobuf);
575         if (err)
576                 goto out;
577
578 #if defined(SEEK_DATA) && defined(SEEK_HOLE)
579         err = try_lseek_copy(fs, fd, statbuf, e2_file, buf, zerobuf);
580         if (err != EXT2_ET_UNIMPLEMENTED)
581                 goto out;
582 #endif
583
584 #if defined(FS_IOC_FIEMAP)
585         err = try_fiemap_copy(fs, fd, e2_file, buf, zerobuf);
586         if (err != EXT2_ET_UNIMPLEMENTED)
587                 goto out;
588 #endif
589
590         err = copy_file_chunk(fs, fd, e2_file, 0, statbuf->st_size, buf,
591                               zerobuf);
592 out:
593         ext2fs_free_mem(&zerobuf);
594         ext2fs_free_mem(&buf);
595         close_err = ext2fs_file_close(e2_file);
596         if (err == 0)
597                 err = close_err;
598         return err;
599 }
600
601 static int is_hardlink(struct hdlinks_s *hdlinks, dev_t dev, ino_t ino)
602 {
603         int i;
604
605         for (i = 0; i < hdlinks->count; i++) {
606                 if (hdlinks->hdl[i].src_dev == dev &&
607                     hdlinks->hdl[i].src_ino == ino)
608                         return i;
609         }
610         return -1;
611 }
612
613 /* Copy the native file to the fs */
614 errcode_t do_write_internal(ext2_filsys fs, ext2_ino_t cwd, const char *src,
615                             const char *dest, ext2_ino_t root)
616 {
617         int             fd;
618         struct stat     statbuf;
619         ext2_ino_t      newfile;
620         errcode_t       retval;
621         struct ext2_inode inode;
622
623         fd = ext2fs_open_file(src, O_RDONLY, 0);
624         if (fd < 0) {
625                 retval = errno;
626                 com_err(__func__, retval, _("while opening \"%s\" to copy"),
627                         src);
628                 return retval;
629         }
630         if (fstat(fd, &statbuf) < 0) {
631                 retval = errno;
632                 goto out;
633         }
634
635         retval = ext2fs_namei(fs, root, cwd, dest, &newfile);
636         if (retval == 0) {
637                 retval = EXT2_ET_FILE_EXISTS;
638                 goto out;
639         }
640
641         retval = ext2fs_new_inode(fs, cwd, 010755, 0, &newfile);
642         if (retval)
643                 goto out;
644 #ifdef DEBUGFS
645         printf("Allocated inode: %u\n", newfile);
646 #endif
647         retval = ext2fs_link(fs, cwd, dest, newfile,
648                                 EXT2_FT_REG_FILE);
649         if (retval == EXT2_ET_DIR_NO_SPACE) {
650                 retval = ext2fs_expand_dir(fs, cwd);
651                 if (retval)
652                         goto out;
653                 retval = ext2fs_link(fs, cwd, dest, newfile,
654                                         EXT2_FT_REG_FILE);
655         }
656         if (retval)
657                 goto out;
658         if (ext2fs_test_inode_bitmap2(fs->inode_map, newfile))
659                 com_err(__func__, 0, "Warning: inode already set");
660         ext2fs_inode_alloc_stats2(fs, newfile, +1, 0);
661         memset(&inode, 0, sizeof(inode));
662         inode.i_mode = (statbuf.st_mode & ~LINUX_S_IFMT) | LINUX_S_IFREG;
663         inode.i_atime = inode.i_ctime = inode.i_mtime =
664                 fs->now ? fs->now : time(0);
665         inode.i_links_count = 1;
666         retval = ext2fs_inode_size_set(fs, &inode, statbuf.st_size);
667         if (retval)
668                 goto out;
669         if (ext2fs_has_feature_inline_data(fs->super)) {
670                 inode.i_flags |= EXT4_INLINE_DATA_FL;
671         } else if (ext2fs_has_feature_extents(fs->super)) {
672                 ext2_extent_handle_t handle;
673
674                 inode.i_flags &= ~EXT4_EXTENTS_FL;
675                 retval = ext2fs_extent_open2(fs, newfile, &inode, &handle);
676                 if (retval)
677                         goto out;
678                 ext2fs_extent_free(handle);
679         }
680
681         retval = ext2fs_write_new_inode(fs, newfile, &inode);
682         if (retval)
683                 goto out;
684         if (inode.i_flags & EXT4_INLINE_DATA_FL) {
685                 retval = ext2fs_inline_data_init(fs, newfile);
686                 if (retval)
687                         goto out;
688         }
689         if (LINUX_S_ISREG(inode.i_mode)) {
690                 retval = copy_file(fs, fd, &statbuf, newfile);
691                 if (retval)
692                         goto out;
693         }
694 out:
695         close(fd);
696         return retval;
697 }
698
699 struct file_info {
700         char *path;
701         size_t path_len;
702         size_t path_max_len;
703 };
704
705 static errcode_t path_append(struct file_info *target, const char *file)
706 {
707         if (strlen(file) + target->path_len + 1 > target->path_max_len) {
708                 void *p;
709                 target->path_max_len *= 2;
710                 p = realloc(target->path, target->path_max_len);
711                 if (p == NULL)
712                         return EXT2_ET_NO_MEMORY;
713                 target->path = p;
714         }
715         target->path_len += sprintf(target->path + target->path_len, "/%s",
716                                     file);
717         return 0;
718 }
719
720 /* Copy files from source_dir to fs */
721 static errcode_t __populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
722                                const char *source_dir, ext2_ino_t root,
723                                struct hdlinks_s *hdlinks,
724                                struct file_info *target,
725                                struct fs_ops_callbacks *fs_callbacks)
726 {
727         const char      *name;
728         DIR             *dh;
729         struct dirent   *dent;
730         struct stat     st;
731         char            *ln_target = NULL;
732         unsigned int    save_inode;
733         ext2_ino_t      ino;
734         errcode_t       retval = 0;
735         int             read_cnt;
736         int             hdlink;
737         size_t          cur_dir_path_len;
738
739         if (chdir(source_dir) < 0) {
740                 retval = errno;
741                 com_err(__func__, retval,
742                         _("while changing working directory to \"%s\""),
743                         source_dir);
744                 return retval;
745         }
746
747         if (!(dh = opendir("."))) {
748                 retval = errno;
749                 com_err(__func__, retval,
750                         _("while opening directory \"%s\""), source_dir);
751                 return retval;
752         }
753
754         while ((dent = readdir(dh))) {
755                 if ((!strcmp(dent->d_name, ".")) ||
756                     (!strcmp(dent->d_name, "..")))
757                         continue;
758                 if (lstat(dent->d_name, &st)) {
759                         retval = errno;
760                         com_err(__func__, retval, _("while lstat \"%s\""),
761                                 dent->d_name);
762                         goto out;
763                 }
764                 name = dent->d_name;
765
766                 /* Check for hardlinks */
767                 save_inode = 0;
768                 if (!S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode) &&
769                     st.st_nlink > 1) {
770                         hdlink = is_hardlink(hdlinks, st.st_dev, st.st_ino);
771                         if (hdlink >= 0) {
772                                 retval = add_link(fs, parent_ino,
773                                                   hdlinks->hdl[hdlink].dst_ino,
774                                                   name);
775                                 if (retval) {
776                                         com_err(__func__, retval,
777                                                 "while linking %s", name);
778                                         goto out;
779                                 }
780                                 continue;
781                         } else
782                                 save_inode = 1;
783                 }
784
785                 cur_dir_path_len = target->path_len;
786                 retval = path_append(target, name);
787                 if (retval) {
788                         com_err(__func__, retval,
789                                 "while appending %s", name);
790                         goto out;
791                 }
792
793                 if (fs_callbacks && fs_callbacks->create_new_inode) {
794                         retval = fs_callbacks->create_new_inode(fs,
795                                 target->path, name, parent_ino, root,
796                                 st.st_mode & S_IFMT);
797                         if (retval)
798                                 goto out;
799                 }
800
801                 switch(st.st_mode & S_IFMT) {
802                 case S_IFCHR:
803                 case S_IFBLK:
804                 case S_IFIFO:
805 #ifndef _WIN32
806                 case S_IFSOCK:
807                         retval = do_mknod_internal(fs, parent_ino, name,
808                                                    st.st_mode, st.st_rdev);
809                         if (retval) {
810                                 com_err(__func__, retval,
811                                         _("while creating special file "
812                                           "\"%s\""), name);
813                                 goto out;
814                         }
815                         break;
816                 case S_IFLNK:
817                         ln_target = malloc(st.st_size + 1);
818                         if (ln_target == NULL) {
819                                 com_err(__func__, retval,
820                                         _("malloc failed"));
821                                 goto out;
822                         }
823                         read_cnt = readlink(name, ln_target,
824                                             st.st_size + 1);
825                         if (read_cnt == -1) {
826                                 retval = errno;
827                                 com_err(__func__, retval,
828                                         _("while trying to read link \"%s\""),
829                                         name);
830                                 free(ln_target);
831                                 goto out;
832                         }
833                         if (read_cnt > st.st_size) {
834                                 com_err(__func__, retval,
835                                         _("symlink increased in size "
836                                           "between lstat() and readlink()"));
837                                 free(ln_target);
838                                 goto out;
839                         }
840                         ln_target[read_cnt] = '\0';
841                         retval = do_symlink_internal(fs, parent_ino, name,
842                                                      ln_target, root);
843                         free(ln_target);
844                         if (retval) {
845                                 com_err(__func__, retval,
846                                         _("while writing symlink\"%s\""),
847                                         name);
848                                 goto out;
849                         }
850                         break;
851 #endif
852                 case S_IFREG:
853                         retval = do_write_internal(fs, parent_ino, name, name,
854                                                    root);
855                         if (retval) {
856                                 com_err(__func__, retval,
857                                         _("while writing file \"%s\""), name);
858                                 goto out;
859                         }
860                         break;
861                 case S_IFDIR:
862                         /* Don't choke on /lost+found */
863                         if (parent_ino == EXT2_ROOT_INO &&
864                             strcmp(name, "lost+found") == 0)
865                                 goto find_lnf;
866                         retval = do_mkdir_internal(fs, parent_ino, name,
867                                                    root);
868                         if (retval) {
869                                 com_err(__func__, retval,
870                                         _("while making dir \"%s\""), name);
871                                 goto out;
872                         }
873 find_lnf:
874                         retval = ext2fs_namei(fs, root, parent_ino,
875                                               name, &ino);
876                         if (retval) {
877                                 com_err(name, retval, 0);
878                                         goto out;
879                         }
880                         /* Populate the dir recursively*/
881                         retval = __populate_fs(fs, ino, name, root, hdlinks,
882                                                target, fs_callbacks);
883                         if (retval)
884                                 goto out;
885                         if (chdir("..")) {
886                                 retval = errno;
887                                 com_err(__func__, retval,
888                                         _("while changing directory"));
889                                 goto out;
890                         }
891                         break;
892                 default:
893                         com_err(__func__, 0,
894                                 _("ignoring entry \"%s\""), name);
895                 }
896
897                 retval =  ext2fs_namei(fs, root, parent_ino, name, &ino);
898                 if (retval) {
899                         com_err(name, retval, _("while looking up \"%s\""),
900                                 name);
901                         goto out;
902                 }
903
904                 retval = set_inode_extra(fs, ino, &st);
905                 if (retval) {
906                         com_err(__func__, retval,
907                                 _("while setting inode for \"%s\""), name);
908                         goto out;
909                 }
910
911                 retval = set_inode_xattr(fs, ino, name);
912                 if (retval) {
913                         com_err(__func__, retval,
914                                 _("while setting xattrs for \"%s\""), name);
915                         goto out;
916                 }
917
918                 if (fs_callbacks && fs_callbacks->end_create_new_inode) {
919                         retval = fs_callbacks->end_create_new_inode(fs,
920                                 target->path, name, parent_ino, root,
921                                 st.st_mode & S_IFMT);
922                         if (retval)
923                                 goto out;
924                 }
925
926                 /* Save the hardlink ino */
927                 if (save_inode) {
928                         /*
929                          * Check whether need more memory, and we don't need
930                          * free() since the lifespan will be over after the fs
931                          * populated.
932                          */
933                         if (hdlinks->count == hdlinks->size) {
934                                 void *p = realloc(hdlinks->hdl,
935                                                 (hdlinks->size + HDLINK_CNT) *
936                                                 sizeof(struct hdlink_s));
937                                 if (p == NULL) {
938                                         retval = EXT2_ET_NO_MEMORY;
939                                         com_err(name, retval,
940                                                 _("while saving inode data"));
941                                         goto out;
942                                 }
943                                 hdlinks->hdl = p;
944                                 hdlinks->size += HDLINK_CNT;
945                         }
946                         hdlinks->hdl[hdlinks->count].src_dev = st.st_dev;
947                         hdlinks->hdl[hdlinks->count].src_ino = st.st_ino;
948                         hdlinks->hdl[hdlinks->count].dst_ino = ino;
949                         hdlinks->count++;
950                 }
951                 target->path_len = cur_dir_path_len;
952                 target->path[target->path_len] = 0;
953         }
954
955 out:
956         closedir(dh);
957         return retval;
958 }
959
960 errcode_t populate_fs2(ext2_filsys fs, ext2_ino_t parent_ino,
961                        const char *source_dir, ext2_ino_t root,
962                        struct fs_ops_callbacks *fs_callbacks)
963 {
964         struct file_info file_info;
965         struct hdlinks_s hdlinks;
966         errcode_t retval;
967
968         if (!(fs->flags & EXT2_FLAG_RW)) {
969                 com_err(__func__, 0, "Filesystem opened readonly");
970                 return EROFS;
971         }
972
973         hdlinks.count = 0;
974         hdlinks.size = HDLINK_CNT;
975         hdlinks.hdl = realloc(NULL, hdlinks.size * sizeof(struct hdlink_s));
976         if (hdlinks.hdl == NULL) {
977                 retval = errno;
978                 com_err(__func__, retval, _("while allocating memory"));
979                 return retval;
980         }
981
982         file_info.path_len = 0;
983         file_info.path_max_len = 255;
984         file_info.path = calloc(file_info.path_max_len, 1);
985
986         retval = __populate_fs(fs, parent_ino, source_dir, root, &hdlinks,
987                                &file_info, fs_callbacks);
988
989         free(file_info.path);
990         free(hdlinks.hdl);
991         return retval;
992 }
993
994 errcode_t populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
995                       const char *source_dir, ext2_ino_t root)
996 {
997         return populate_fs2(fs, parent_ino, source_dir, root, NULL);
998 }