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