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