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