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