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 
ext2_file_type(unsigned int mode)35 static 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 */
add_link(ext2_filsys fs,ext2_ino_t parent_ino,ext2_ino_t ino,const char * name)62 static 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 */
set_inode_extra(ext2_filsys fs,ext2_ino_t ino,struct stat * st)101 static 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
set_inode_xattr(ext2_filsys fs,ext2_ino_t ino,const char * filename)127 static 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 */
set_inode_xattr(ext2_filsys fs EXT2FS_ATTR ((unused)),ext2_ino_t ino EXT2FS_ATTR ((unused)),const char * filename EXT2FS_ATTR ((unused)))218 static 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  */
do_mknod_internal(ext2_filsys fs,ext2_ino_t cwd,const char * name,struct stat * st)227 errcode_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 */
do_symlink_internal(ext2_filsys fs,ext2_ino_t cwd,const char * name,char * target,ext2_ino_t root)312 errcode_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 */
do_mkdir_internal(ext2_filsys fs,ext2_ino_t cwd,const char * name,ext2_ino_t root)348 errcode_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
my_pread(int fd,void * buf,size_t count,off_t offset)386 static 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 
copy_file_range(ext2_filsys fs,int fd,ext2_file_t e2_file,off_t start,off_t end,char * buf,char * zerobuf)395 static 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 	}
443 fail:
444 	return err;
445 }
446 
447 #if defined(SEEK_DATA) && defined(SEEK_HOLE)
try_lseek_copy(ext2_filsys fs,int fd,struct stat * statbuf,ext2_file_t e2_file,char * buf,char * zerobuf)448 static 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)
try_fiemap_copy(ext2_filsys fs,int fd,ext2_file_t e2_file,char * buf,char * zerobuf)482 static 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));
535 out:
536 	ext2fs_free_mem(&fiemap_buf);
537 	return err;
538 }
539 #endif /* FS_IOC_FIEMAP */
540 
copy_file(ext2_filsys fs,int fd,struct stat * statbuf,ext2_ino_t ino)541 static 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);
574 out:
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 
is_hardlink(struct hdlinks_s * hdlinks,dev_t dev,ino_t ino)583 static 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 */
do_write_internal(ext2_filsys fs,ext2_ino_t cwd,const char * src,const char * dest,ext2_ino_t root)596 errcode_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 	}
676 out:
677 	close(fd);
678 	return retval;
679 }
680 
681 struct file_info {
682 	char *path;
683 	size_t path_len;
684 	size_t path_max_len;
685 };
686 
path_append(struct file_info * target,const char * file)687 static 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 */
__populate_fs(ext2_filsys fs,ext2_ino_t parent_ino,const char * source_dir,ext2_ino_t root,struct hdlinks_s * hdlinks,struct file_info * target,struct fs_ops_callbacks * fs_callbacks)701 static 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 			}
847 find_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 
929 out:
930 	closedir(dh);
931 	return retval;
932 }
933 
populate_fs2(ext2_filsys fs,ext2_ino_t parent_ino,const char * source_dir,ext2_ino_t root,struct fs_ops_callbacks * fs_callbacks)934 errcode_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 
populate_fs(ext2_filsys fs,ext2_ino_t parent_ino,const char * source_dir,ext2_ino_t root)968 errcode_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