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