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