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