/
githubmirror
/
e2fsprogs
Обзор
Документация
Войти
/
githubmirror
/
e2fsprogs
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
misc/create_inode.c
1 272 строки
31 KB
Theodore Ts'o
Merge branch 'rbt/largefile' of github.com:robertlinux/e2fsprogs into maint
05 мар 2026, 18:37
05 мар 2026, 18:37
22f5c95
Код
Авторство
О чём код?
/* * create_inode.c --- create an inode * * Copyright (C) 2014 Robert Yang <liezhi.yang@windriver.com> * * %Begin-Header% * This file may be redistributed under the terms of the GNU library * General Public License, version 2. * %End-Header% */ #define _LARGEFILE64_SOURCE 1 #define _GNU_SOURCE 1 #include "config.h" #include <time.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <limits.h> /* for PATH_MAX */ #include <dirent.h> /* for scandir() and alphasort() */ #if defined HAVE_SYS_XATTR_H #include <sys/xattr.h> #elif defined HAVE_ATTR_XATTR_H #include <attr/xattr.h> #endif #ifdef HAVE_SYS_IOCTL_H #include <sys/ioctl.h> #endif #ifdef HAVE_SYS_SYSMACROS_H #include <sys/sysmacros.h> #endif #if defined(HAVE_LINUX_FSVERITY_H) && defined(HAVE_FS_IOC_READ_VERITY_METADATA) #include <linux/fsverity.h> #include <linux/fs.h> #endif #include "ext2fs/ext2_fs.h" #include "ext2fs/ext2fs.h" #include "ext2fs/ext2_types.h" #include "ext2fs/fiemap.h" #include "create_inode.h" #include "support/nls-enable.h" #include "create_inode_libarchive.h" /* 64KiB is the minimum blksize to best minimize system call overhead. */ #define COPY_FILE_BUFLEN 65536 int link_append_flag = EXT2FS_LINK_EXPAND; #define COPY_FLAGS_MASK (EXT2_SECRM_FL | EXT2_UNRM_FL | EXT2_COMPR_FL | \ EXT2_SYNC_FL | EXT2_IMMUTABLE_FL | EXT2_APPEND_FL | \ EXT2_NODUMP_FL | EXT2_NOATIME_FL | \ EXT3_JOURNAL_DATA_FL | EXT2_NOTAIL_FL | \ EXT2_DIRSYNC_FL | EXT2_TOPDIR_FL | FS_DAX_FL | \ EXT4_PROJINHERIT_FL | EXT4_CASEFOLD_FL) static int ext2_file_type(unsigned int mode) { if (LINUX_S_ISREG(mode)) return EXT2_FT_REG_FILE; if (LINUX_S_ISDIR(mode)) return EXT2_FT_DIR; if (LINUX_S_ISCHR(mode)) return EXT2_FT_CHRDEV; if (LINUX_S_ISBLK(mode)) return EXT2_FT_BLKDEV; if (LINUX_S_ISLNK(mode)) return EXT2_FT_SYMLINK; if (LINUX_S_ISFIFO(mode)) return EXT2_FT_FIFO; if (LINUX_S_ISSOCK(mode)) return EXT2_FT_SOCK; return 0; } /* Link an inode number to a directory */ errcode_t add_link(ext2_filsys fs, ext2_ino_t parent_ino, ext2_ino_t ino, const char *name) { struct ext2_inode inode; errcode_t retval; retval = ext2fs_read_inode(fs, ino, &inode); if (retval) { com_err(__func__, retval, _("while reading inode %u"), ino); return retval; } retval = ext2fs_link(fs, parent_ino, name, ino, ext2_file_type(inode.i_mode) | link_append_flag); if (retval) { com_err(__func__, retval, _("while linking \"%s\""), name); return retval; } inode.i_links_count++; retval = ext2fs_write_inode(fs, ino, &inode); if (retval) com_err(__func__, retval, _("while writing inode %u"), ino); return retval; } static time_t clamped_time(ext2_filsys fs, time_t t) { if ((fs->flags2 & EXT2_FLAG2_USE_FAKE_TIME) && t > fs->now) return fs->now; return t; } /* Set the uid, gid, mode and time for the inode */ errcode_t set_inode_extra(ext2_filsys fs, ext2_ino_t ino, const struct stat *st) { errcode_t retval; struct ext2_inode inode; retval = ext2fs_read_inode(fs, ino, &inode); if (retval) { com_err(__func__, retval, _("while reading inode %u"), ino); return retval; } inode.i_uid = st->st_uid; ext2fs_set_i_uid_high(inode, st->st_uid >> 16); inode.i_gid = st->st_gid; ext2fs_set_i_gid_high(inode, st->st_gid >> 16); inode.i_mode = (LINUX_S_IFMT & inode.i_mode) | (~S_IFMT & st->st_mode); ext2fs_inode_xtime_set(&inode, i_atime, clamped_time(fs, st->st_atime)); ext2fs_inode_xtime_set(&inode, i_ctime, clamped_time(fs, st->st_ctime)); ext2fs_inode_xtime_set(&inode, i_mtime, clamped_time(fs, st->st_mtime)); retval = ext2fs_write_inode(fs, ino, &inode); if (retval) com_err(__func__, retval, _("while writing inode %u"), ino); return retval; } #ifdef HAVE_LLISTXATTR static errcode_t set_inode_xattr(ext2_filsys fs, ext2_ino_t ino, const char *filename) { errcode_t retval, close_retval; struct ext2_xattr_handle *handle; ssize_t size, value_size; char *list = NULL; int i; size = llistxattr(filename, NULL, 0); if (size == -1) { if (errno == ENOTSUP) return 0; retval = errno; com_err(__func__, retval, _("while listing attributes of \"%s\""), filename); return retval; } else if (size == 0) { return 0; } retval = ext2fs_xattrs_open(fs, ino, &handle); if (retval) { if (retval == EXT2_ET_MISSING_EA_FEATURE) return 0; com_err(__func__, retval, _("while opening inode %u"), ino); return retval; } retval = ext2fs_xattrs_read(handle); if (retval) { com_err(__func__, retval, _("while reading xattrs for inode %u"), ino); goto out; } retval = ext2fs_get_mem(size, &list); if (retval) { com_err(__func__, retval, _("while allocating memory")); goto out; } size = llistxattr(filename, list, size); if (size == -1) { retval = errno; com_err(__func__, retval, _("while listing attributes of \"%s\""), filename); goto out; } for (i = 0; i < size; i += strlen(&list[i]) + 1) { const char *name = &list[i]; char *value; value_size = lgetxattr(filename, name, NULL, 0); if (value_size == -1) { retval = errno; com_err(__func__, retval, _("while reading attribute \"%s\" of \"%s\""), name, filename); break; } retval = ext2fs_get_mem(value_size, &value); if (retval) { com_err(__func__, retval, _("while allocating memory")); break; } value_size = lgetxattr(filename, name, value, value_size); if (value_size == -1) { ext2fs_free_mem(&value); retval = errno; com_err(__func__, retval, _("while reading attribute \"%s\" of \"%s\""), name, filename); break; } retval = ext2fs_xattr_set(handle, name, value, value_size); ext2fs_free_mem(&value); if (retval) { com_err(__func__, retval, _("while writing attribute \"%s\" to inode %u"), name, ino); break; } } out: ext2fs_free_mem(&list); close_retval = ext2fs_xattrs_close(&handle); if (close_retval) { com_err(__func__, retval, _("while closing inode %u"), ino); retval = retval ? retval : close_retval; } return retval; } #else /* HAVE_LLISTXATTR */ static errcode_t set_inode_xattr(ext2_filsys fs EXT2FS_ATTR((unused)), ext2_ino_t ino EXT2FS_ATTR((unused)), const char *filename EXT2FS_ATTR((unused))) { return 0; } #endif /* HAVE_LLISTXATTR */ #ifndef _WIN32 /* Make a special files (block and character devices), fifo's, and sockets */ errcode_t do_mknod_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name, unsigned int st_mode, unsigned int st_rdev) { ext2_ino_t ino; errcode_t retval; struct ext2_inode inode; unsigned long devmajor, devminor, mode; int filetype; time_t now; switch(st_mode & S_IFMT) { case S_IFCHR: mode = LINUX_S_IFCHR; filetype = EXT2_FT_CHRDEV; break; case S_IFBLK: mode = LINUX_S_IFBLK; filetype = EXT2_FT_BLKDEV; break; case S_IFIFO: mode = LINUX_S_IFIFO; filetype = EXT2_FT_FIFO; break; #ifndef _WIN32 case S_IFSOCK: mode = LINUX_S_IFSOCK; filetype = EXT2_FT_SOCK; break; #endif default: return EXT2_ET_INVALID_ARGUMENT; } retval = ext2fs_new_inode(fs, cwd, 010755, 0, &ino); if (retval) { com_err(__func__, retval, _("while allocating inode \"%s\""), name); return retval; } #ifdef DEBUGFS printf("Allocated inode: %u\n", ino); #endif retval = ext2fs_link(fs, cwd, name, ino, filetype | link_append_flag); if (retval) { com_err(name, retval, _("while creating inode \"%s\""), name); return retval; } if (ext2fs_test_inode_bitmap2(fs->inode_map, ino)) com_err(__func__, 0, "Warning: inode already set"); ext2fs_inode_alloc_stats2(fs, ino, +1, 0); memset(&inode, 0, sizeof(inode)); inode.i_mode = mode; now = fs->now ? fs->now : time(0); ext2fs_inode_xtime_set(&inode, i_atime, now); ext2fs_inode_xtime_set(&inode, i_ctime, now); ext2fs_inode_xtime_set(&inode, i_mtime, now); if (filetype != S_IFIFO) { devmajor = major(st_rdev); devminor = minor(st_rdev); if ((devmajor < 256) && (devminor < 256)) { inode.i_block[0] = devmajor * 256 + devminor; inode.i_block[1] = 0; } else { inode.i_block[0] = 0; inode.i_block[1] = (devminor & 0xff) | (devmajor << 8) | ((devminor & ~0xff) << 12); } } inode.i_links_count = 1; retval = ext2fs_write_new_inode(fs, ino, &inode); if (retval) com_err(__func__, retval, _("while writing inode %u"), ino); return retval; } #endif /* Make a symlink name -> target */ errcode_t do_symlink_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name, char *target, ext2_ino_t root) { char *cp; ext2_ino_t parent_ino; errcode_t retval; cp = strrchr(name, '/'); if (cp) { *cp = 0; retval = ext2fs_namei(fs, root, cwd, name, &parent_ino); if (retval) { com_err(name, retval, 0); return retval; } name = cp+1; } else parent_ino = cwd; retval = ext2fs_symlink(fs, parent_ino, 0, name, target); if (retval == EXT2_ET_DIR_NO_SPACE) { retval = ext2fs_expand_dir(fs, parent_ino); if (retval) { com_err("do_symlink_internal", retval, _("while expanding directory")); return retval; } retval = ext2fs_symlink(fs, parent_ino, 0, name, target); } if (retval) com_err("ext2fs_symlink", retval, _("while creating symlink \"%s\""), name); return retval; } /* Make a directory in the fs */ errcode_t do_mkdir_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name, unsigned long flags, ext2_ino_t root) { char *cp; ext2_ino_t parent_ino; errcode_t retval; cp = strrchr(name, '/'); if (cp) { *cp = 0; retval = ext2fs_namei(fs, root, cwd, name, &parent_ino); if (retval) { com_err(name, retval, _("while looking up \"%s\""), name); return retval; } name = cp+1; } else parent_ino = cwd; retval = ext2fs_mkdir2(fs, parent_ino, 0, (flags & COPY_FLAGS_MASK), link_append_flag, name, NULL); if (retval) com_err("ext2fs_mkdir2", retval, _("while creating directory \"%s\""), name); return retval; } #if !defined HAVE_PREAD64 && !defined HAVE_PREAD static ssize_t my_pread(int fd, void *buf, size_t count, off_t offset) { if (lseek(fd, offset, SEEK_SET) < 0) return 0; return read(fd, buf, count); } #endif /* !defined HAVE_PREAD64 && !defined HAVE_PREAD */ static errcode_t write_all(ext2_file_t e2_file, __u64 off, const char *buf, unsigned int n_bytes) { errcode_t err = ext2fs_file_llseek(e2_file, off, EXT2_SEEK_SET, NULL); if (err) return err; const char *ptr = buf; while (n_bytes) { unsigned int written; err = ext2fs_file_write(e2_file, ptr, n_bytes, &written); if (err) return err; if (written == 0) return EIO; n_bytes -= written; ptr += written; } return 0; } static errcode_t copy_file_chunk(ext2_filsys fs, int fd, ext2_file_t e2_file, off_t start, off_t end, char *buf, char *zerobuf) { off_t off, bpos; ssize_t got, blen; char *ptr; errcode_t err = 0; for (off = start; off < end; off += COPY_FILE_BUFLEN) { #ifdef HAVE_PREAD64 got = pread64(fd, buf, COPY_FILE_BUFLEN, off); #elif HAVE_PREAD got = pread(fd, buf, COPY_FILE_BUFLEN, off); #else got = my_pread(fd, buf, COPY_FILE_BUFLEN, off); #endif if (got < 0) { err = errno; goto fail; } for (bpos = 0, ptr = buf; bpos < got; bpos += fs->blocksize) { blen = fs->blocksize; if (blen > got - bpos) blen = got - bpos; if (memcmp(ptr, zerobuf, blen) == 0) { ptr += blen; continue; } err = write_all(e2_file, off + bpos, ptr, blen); if (err) goto fail; ptr += blen; } } fail: return err; } #if defined(SEEK_DATA) && defined(SEEK_HOLE) static errcode_t try_lseek_copy(ext2_filsys fs, int fd, struct stat *statbuf, ext2_file_t e2_file, char *buf, char *zerobuf) { off_t data = 0, hole; off_t data_blk, hole_blk; errcode_t err = 0; /* Try to use SEEK_DATA and SEEK_HOLE */ while (data < statbuf->st_size) { data = lseek(fd, data, SEEK_DATA); if (data < 0) { if (errno == ENXIO) break; return EXT2_ET_UNIMPLEMENTED; } hole = lseek(fd, data, SEEK_HOLE); if (hole < 0) return EXT2_ET_UNIMPLEMENTED; data_blk = data & ~(off_t)(fs->blocksize - 1); hole_blk = ((hole + (off_t)(fs->blocksize - 1)) & ~(off_t)(fs->blocksize - 1)); err = copy_file_chunk(fs, fd, e2_file, data_blk, hole_blk, buf, zerobuf); if (err) return err; data = hole; } return err; } #endif /* SEEK_DATA and SEEK_HOLE */ #if defined(FS_IOC_FIEMAP) static errcode_t try_fiemap_copy(ext2_filsys fs, int fd, ext2_file_t e2_file, char *buf, char *zerobuf) { #define EXTENT_MAX_COUNT 512 struct fiemap *fiemap_buf; struct fiemap_extent *ext_buf, *ext; int ext_buf_size, fie_buf_size; off_t pos = 0; unsigned int i; errcode_t err; ext_buf_size = EXTENT_MAX_COUNT * sizeof(struct fiemap_extent); fie_buf_size = sizeof(struct fiemap) + ext_buf_size; err = ext2fs_get_memzero(fie_buf_size, &fiemap_buf); if (err) return err; ext_buf = fiemap_buf->fm_extents; memset(fiemap_buf, 0, fie_buf_size); fiemap_buf->fm_length = FIEMAP_MAX_OFFSET; fiemap_buf->fm_flags |= FIEMAP_FLAG_SYNC; fiemap_buf->fm_extent_count = EXTENT_MAX_COUNT; do { fiemap_buf->fm_start = pos; memset(ext_buf, 0, ext_buf_size); err = ioctl(fd, FS_IOC_FIEMAP, fiemap_buf); if (err < 0 && (errno == EOPNOTSUPP || errno == ENOTTY)) { err = EXT2_ET_UNIMPLEMENTED; goto out; } else if (err < 0) { err = errno; goto out; } else if (fiemap_buf->fm_mapped_extents == 0) goto out; for (i = 0, ext = ext_buf; i < fiemap_buf->fm_mapped_extents; i++, ext++) { err = copy_file_chunk(fs, fd, e2_file, ext->fe_logical, ext->fe_logical + ext->fe_length, buf, zerobuf); if (err) goto out; } ext--; /* Record file's logical offset this time */ pos = ext->fe_logical + ext->fe_length; /* * If fm_extents array has been filled and * there are extents left, continue to cycle. */ } while (fiemap_buf->fm_mapped_extents == EXTENT_MAX_COUNT && !(ext->fe_flags & FIEMAP_EXTENT_LAST)); out: ext2fs_free_mem(&fiemap_buf); return err; } #endif /* FS_IOC_FIEMAP */ #if defined(HAVE_LINUX_FSVERITY_H) && defined(HAVE_FS_IOC_READ_VERITY_METADATA) static inline off_t round_up(off_t n, off_t blksz, off_t bias) { return ((n - bias + (blksz - 1)) & ~(blksz - 1)) + bias; } static errcode_t copy_fs_verity_data(ext2_file_t e2_file, ext2_off_t e2_offset, int fd, uint64_t metadata_type, __u32 *written) { errcode_t err; char buf[COPY_FILE_BUFLEN]; int size; *written = 0; do { struct fsverity_read_metadata_arg arg = { .metadata_type = metadata_type, #if (SIZEOF_VOID_P == 4) .buf_ptr = (uint32_t) buf, #else .buf_ptr = (uint64_t) buf, #endif .length = sizeof(buf), .offset = *written, }; size = ioctl(fd, FS_IOC_READ_VERITY_METADATA, &arg); if (size < 0) { if (errno == EINTR) continue; return errno; } /* * In order to support copying in signature blob, we * need to set the descriptor's sig_size field (which * is the __reserved_0x04 field in the userspace * version of the structure if there is a signature * blob. To deal with this, we rely on the fact that * size of the fsverity descriptor (256 bytes) is * significantly smaller than COPY_FILE_BUFLEN (64k * bytes), and that combined size descriptor and * signature blob will fit in COPY_FILE_BUFLEN. */ if (metadata_type == FS_VERITY_METADATA_TYPE_DESCRIPTOR) { int sig_size; struct fsverity_descriptor *desc = (void *) buf; arg.metadata_type = FS_VERITY_METADATA_TYPE_SIGNATURE; arg.buf_ptr += size; arg.length -= size; arg.offset = 0; sig_size = ioctl(fd, FS_IOC_READ_VERITY_METADATA, &arg); if (sig_size > 0) { desc->__reserved_0x04 = ext2fs_cpu_to_le32(sig_size); size += sig_size; } err = write_all(e2_file, e2_offset, buf, size); if (err == 0) *written += size; return err; } err = write_all(e2_file, e2_offset, buf, size); if (err) return err; e2_offset += size; *written += size; } while (size != 0); return 0; } static errcode_t copy_fs_verity(ext2_filsys fs, int fd, ext2_file_t e2_file, off_t st_size) { ext2_ino_t ino = ext2fs_file_get_inode_num(e2_file); struct ext2_inode *inode = ext2fs_file_get_inode(e2_file); off_t offset = round_up(st_size, 65536, 0); __u32 written; errcode_t err; if (!ino || !inode || !(inode->i_flags & EXT4_EXTENTS_FL) || (inode->i_flags & EXT4_INLINE_DATA_FL)) return 0; /* We read the existing fs-verity data out of the host * filesystem and write it verbatim into the file we're * creating, as blocks following the end of the file. * * https://docs.kernel.org/filesystems/fsverity.html#fs-ioc-read-verity-metadata * https://docs.kernel.org/filesystems/ext4/overview.html#verity-files */ /* Copy Merkel tree data: might be empty (for empty files) */ err = copy_fs_verity_data(e2_file, offset, fd, FS_VERITY_METADATA_TYPE_MERKLE_TREE, &written); /* These errors are if the file/filesystem/kernel doesn't have * fs-verity. If those happened before we wrote anything, * then we already did the right thing. */ if ((err == ENODATA || err == ENOTTY || err == ENOTSUP) && !written) return 0; if (err) return err; offset = round_up(offset+written, fs->blocksize, 0); /* * Write the verity descriptor, starting at the next file * system block boundary */ err = copy_fs_verity_data(e2_file, offset, fd, FS_VERITY_METADATA_TYPE_DESCRIPTOR, &written); offset = round_up(offset+written, fs->blocksize, -4); /* * Write the size of the verity descriptor in bytes, as a * 4-byte little endian integer. */ written = ext2fs_cpu_to_le32(written); err = write_all(e2_file, offset, (const char *) &written, 4); if (err) return err; /* Reset size in the inode to the original file size */; ext2fs_inode_size_set(fs, inode, st_size); inode->i_flags |= EXT4_VERITY_FL; return ext2fs_write_inode(fs, ino, inode); } #endif static errcode_t copy_file(ext2_filsys fs, int fd, struct stat *statbuf, unsigned long flags, ext2_ino_t ino) { ext2_file_t e2_file; char *buf = NULL, *zerobuf = NULL; errcode_t err, close_err; err = ext2fs_file_open(fs, ino, EXT2_FILE_WRITE, &e2_file); if (err) return err; err = ext2fs_get_mem(COPY_FILE_BUFLEN, &buf); if (err) goto out; err = ext2fs_get_memzero(fs->blocksize, &zerobuf); if (err) goto out; err = EXT2_ET_UNIMPLEMENTED; #if defined(SEEK_DATA) && defined(SEEK_HOLE) err = try_lseek_copy(fs, fd, statbuf, e2_file, buf, zerobuf); #endif #if defined(FS_IOC_FIEMAP) if (err == EXT2_ET_UNIMPLEMENTED) err = try_fiemap_copy(fs, fd, e2_file, buf, zerobuf); #endif if (err == EXT2_ET_UNIMPLEMENTED) err = copy_file_chunk(fs, fd, e2_file, 0, statbuf->st_size, buf, zerobuf); #if defined(HAVE_LINUX_FSVERITY_H) && defined(HAVE_FS_IOC_READ_VERITY_METADATA) if (!err && (flags & EXT4_VERITY_FL)) err = copy_fs_verity(fs, fd, e2_file, statbuf->st_size); #endif out: ext2fs_free_mem(&zerobuf); ext2fs_free_mem(&buf); close_err = ext2fs_file_close(e2_file); if (err == 0) err = close_err; return err; } static int is_hardlink(struct hdlinks_s *hdlinks, dev_t dev, ino_t ino) { int i; for (i = 0; i < hdlinks->count; i++) { if (hdlinks->hdl[i].src_dev == dev && hdlinks->hdl[i].src_ino == ino) return i; } return -1; } /* Copy the native file to the fs */ errcode_t do_write_internal(ext2_filsys fs, ext2_ino_t cwd, const char *src, const char *dest, unsigned long flags, ext2_ino_t root) { int fd; struct stat statbuf; ext2_ino_t newfile, parent_ino; errcode_t retval; struct ext2_inode inode; char *cp; time_t now; fd = ext2fs_open_file(src, O_RDONLY, 0); if (fd < 0) { retval = errno; com_err(__func__, retval, _("while opening \"%s\" to copy"), src); return retval; } if (fstat(fd, &statbuf) < 0) { retval = errno; goto out; } cp = strrchr(dest, '/'); if (cp) { *cp = 0; retval = ext2fs_namei(fs, root, cwd, dest, &parent_ino); if (retval) { com_err(dest, retval, _("while looking up \"%s\""), dest); goto out; } dest = cp+1; } else parent_ino = cwd; retval = ext2fs_namei(fs, root, parent_ino, dest, &newfile); if (retval == 0) { retval = EXT2_ET_FILE_EXISTS; goto out; } retval = ext2fs_new_inode(fs, parent_ino, 010755, 0, &newfile); if (retval) goto out; #ifdef DEBUGFS printf("Allocated inode: %u\n", newfile); #endif retval = ext2fs_link(fs, parent_ino, dest, newfile, EXT2_FT_REG_FILE | link_append_flag); if (retval) goto out; if (ext2fs_test_inode_bitmap2(fs->inode_map, newfile)) com_err(__func__, 0, "Warning: inode already set"); ext2fs_inode_alloc_stats2(fs, newfile, +1, 0); memset(&inode, 0, sizeof(inode)); inode.i_mode = (statbuf.st_mode & ~S_IFMT) | LINUX_S_IFREG; now = fs->now ? fs->now : time(0); ext2fs_inode_xtime_set(&inode, i_atime, now); ext2fs_inode_xtime_set(&inode, i_ctime, now); ext2fs_inode_xtime_set(&inode, i_mtime, now); inode.i_links_count = 1; retval = ext2fs_inode_size_set(fs, &inode, statbuf.st_size); if (retval) goto out; if (ext2fs_has_feature_inline_data(fs->super)) { inode.i_flags |= EXT4_INLINE_DATA_FL; } else if (ext2fs_has_feature_extents(fs->super)) { ext2_extent_handle_t handle; inode.i_flags &= ~EXT4_EXTENTS_FL; retval = ext2fs_extent_open2(fs, newfile, &inode, &handle); if (retval) goto out; ext2fs_extent_free(handle); } inode.i_flags |= (flags & COPY_FLAGS_MASK); retval = ext2fs_write_new_inode(fs, newfile, &inode); if (retval) goto out; if (inode.i_flags & EXT4_INLINE_DATA_FL) { retval = ext2fs_inline_data_init(fs, newfile); if (retval) goto out; } if (LINUX_S_ISREG(inode.i_mode)) { retval = copy_file(fs, fd, &statbuf, flags, newfile); if (retval) goto out; } out: close(fd); return retval; } static errcode_t path_append(struct file_info *target, const char *file) { if (strlen(file) + target->path_len + 1 > target->path_max_len) { void *p; target->path_max_len *= 2; p = realloc(target->path, target->path_max_len); if (p == NULL) return EXT2_ET_NO_MEMORY; target->path = p; } target->path_len += sprintf(target->path + target->path_len, "/%s", file); return 0; } #ifdef _WIN32 static int scandir(const char *dir_name, struct dirent ***name_list, int (*filter)(const struct dirent*), int (*compar)(const struct dirent**, const struct dirent**)) { DIR *dir; struct dirent *dent; struct dirent **temp_list = NULL; size_t temp_list_size = 0; // unit: num of dirent size_t num_dent = 0; dir = opendir(dir_name); if (dir == NULL) { return -1; } while ((dent = readdir(dir))) { if (filter != NULL && !(*filter)(dent)) continue; // re-allocate the list if (num_dent == temp_list_size) { size_t new_list_size = temp_list_size + 32; struct dirent **new_list = (struct dirent**)realloc( temp_list, new_list_size * sizeof(struct dirent*)); if (new_list == NULL) goto out_err; temp_list_size = new_list_size; temp_list = new_list; } // add the copy of dirent to the list temp_list[num_dent] = (struct dirent*)malloc((dent->d_reclen + 3) & ~3); if (!temp_list[num_dent]) goto out_err; memcpy(temp_list[num_dent], dent, dent->d_reclen); num_dent++; } closedir(dir); if (compar != NULL) { qsort(temp_list, num_dent, sizeof(struct dirent*), (int (*)(const void*, const void*))compar); } *name_list = temp_list; return num_dent; out_err: closedir(dir); while (num_dent > 0) free(temp_list[--num_dent]); free(temp_list); return -1; } static int alphasort(const struct dirent **a, const struct dirent **b) { return strcoll((*a)->d_name, (*b)->d_name); } #endif /* Copy files from source_dir to fs in alphabetical order */ static errcode_t __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, int flags, struct fs_ops_callbacks *fs_callbacks) { const char *name; struct dirent **dent; struct stat st; unsigned long fl; unsigned int save_inode; ext2_ino_t ino; errcode_t retval = 0; int hdlink; size_t cur_dir_path_len; int i, num_dents; if (chdir(source_dir) < 0) { retval = errno; com_err(__func__, retval, _("while changing working directory to \"%s\""), source_dir); return retval; } num_dents = scandir(".", &dent, NULL, alphasort); if (num_dents < 0) { retval = errno; com_err(__func__, retval, _("while scanning directory \"%s\""), source_dir); return retval; } for (i = 0; i < num_dents; free(dent[i]), i++) { name = dent[i]->d_name; if ((!strcmp(name, ".")) || (!strcmp(name, ".."))) continue; if (lstat(name, &st)) { retval = errno; com_err(__func__, retval, _("while lstat \"%s\""), name); goto out; } if (fgetflags(name, &fl) < 0) fl = 0; /* Check for hardlinks */ save_inode = 0; if (!S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode) && st.st_nlink > 1) { hdlink = is_hardlink(hdlinks, st.st_dev, st.st_ino); if (hdlink >= 0) { retval = add_link(fs, parent_ino, hdlinks->hdl[hdlink].dst_ino, name); if (retval) { com_err(__func__, retval, "while linking %s", name); goto out; } continue; } else save_inode = 1; } cur_dir_path_len = target->path_len; retval = path_append(target, name); if (retval) { com_err(__func__, retval, "while appending %s", name); goto out; } if (fs_callbacks && fs_callbacks->create_new_inode) { retval = fs_callbacks->create_new_inode(fs, target->path, name, parent_ino, root, st.st_mode & S_IFMT); if (retval) goto out; } switch(st.st_mode & S_IFMT) { case S_IFCHR: case S_IFBLK: case S_IFIFO: #ifndef _WIN32 case S_IFSOCK: retval = do_mknod_internal(fs, parent_ino, name, st.st_mode, st.st_rdev); if (retval) { com_err(__func__, retval, _("while creating special file " "\"%s\""), name); goto out; } break; case S_IFLNK: { char *ln_target; int read_cnt; ln_target = malloc(st.st_size + 1); if (ln_target == NULL) { com_err(__func__, retval, _("malloc failed")); goto out; } read_cnt = readlink(name, ln_target, st.st_size + 1); if (read_cnt == -1) { retval = errno; com_err(__func__, retval, _("while trying to read link \"%s\""), name); free(ln_target); goto out; } if (read_cnt > st.st_size) { com_err(__func__, retval, _("symlink increased in size " "between lstat() and readlink()")); free(ln_target); goto out; } ln_target[read_cnt] = '\0'; retval = do_symlink_internal(fs, parent_ino, name, ln_target, root); free(ln_target); if (retval) { com_err(__func__, retval, _("while writing symlink\"%s\""), name); goto out; } break; } #endif /* !_WIN32 */ case S_IFREG: retval = do_write_internal(fs, parent_ino, name, name, fl, root); if (retval) { com_err(__func__, retval, _("while writing file \"%s\""), name); goto out; } break; case S_IFDIR: /* Don't choke on /lost+found */ if (parent_ino == EXT2_ROOT_INO && strcmp(name, "lost+found") == 0) goto find_lnf; retval = do_mkdir_internal(fs, parent_ino, name, fl, root); if (retval) { com_err(__func__, retval, _("while making dir \"%s\""), name); goto out; } find_lnf: retval = ext2fs_namei(fs, root, parent_ino, name, &ino); if (retval) { com_err(name, retval, 0); goto out; } /* Populate the dir recursively*/ retval = __populate_fs(fs, ino, name, root, hdlinks, target, flags, fs_callbacks); if (retval) goto out; if (chdir("..")) { retval = errno; com_err(__func__, retval, _("while changing directory")); goto out; } break; default: com_err(__func__, 0, _("ignoring entry \"%s\""), name); } retval = ext2fs_namei(fs, root, parent_ino, name, &ino); if (retval) { com_err(name, retval, _("while looking up \"%s\""), name); goto out; } retval = set_inode_extra(fs, ino, &st); if (retval) { com_err(__func__, retval, _("while setting inode for \"%s\""), name); goto out; } if ((flags & POPULATE_FS_NO_COPY_XATTRS) == 0) { retval = set_inode_xattr(fs, ino, name); if (retval) { com_err(__func__, retval, _("while setting xattrs for \"%s\""), name); goto out; } } if (fs_callbacks && fs_callbacks->end_create_new_inode) { retval = fs_callbacks->end_create_new_inode(fs, target->path, name, parent_ino, root, st.st_mode & S_IFMT); if (retval) goto out; } /* Save the hardlink ino */ if (save_inode) { /* * Check whether need more memory, and we don't need * free() since the lifespan will be over after the fs * populated. */ if (hdlinks->count == hdlinks->size) { void *p = realloc(hdlinks->hdl, (hdlinks->size + HDLINK_CNT) * sizeof(struct hdlink_s)); if (p == NULL) { retval = EXT2_ET_NO_MEMORY; com_err(name, retval, _("while saving inode data")); goto out; } hdlinks->hdl = p; hdlinks->size += HDLINK_CNT; } hdlinks->hdl[hdlinks->count].src_dev = st.st_dev; hdlinks->hdl[hdlinks->count].src_ino = st.st_ino; hdlinks->hdl[hdlinks->count].dst_ino = ino; hdlinks->count++; } target->path_len = cur_dir_path_len; target->path[target->path_len] = 0; } out: for (; i < num_dents; free(dent[i]), i++); free(dent); return retval; } errcode_t populate_fs3(ext2_filsys fs, ext2_ino_t parent_ino, const char *source, ext2_ino_t root, int flags, struct fs_ops_callbacks *fs_callbacks) { struct file_info file_info; struct hdlinks_s hdlinks; errcode_t retval; if (!(fs->flags & EXT2_FLAG_RW)) { com_err(__func__, 0, "Filesystem opened readonly"); return EROFS; } hdlinks.count = 0; hdlinks.size = HDLINK_CNT; hdlinks.hdl = realloc(NULL, hdlinks.size * sizeof(struct hdlink_s)); if (hdlinks.hdl == NULL) { retval = errno; com_err(__func__, retval, _("while allocating memory")); return retval; } file_info.path_len = 0; file_info.path_max_len = 255; file_info.path = calloc(file_info.path_max_len, 1); link_append_flag = EXT2FS_LINK_EXPAND; link_append_flag |= (flags & POPULATE_FS_LINK_APPEND) ? EXT2FS_LINK_APPEND : 0; /* interpret input as tarball either if it's "-" (stdin) or if it's * a regular file (or a symlink pointing to a regular file) */ if (strcmp(source, "-") == 0) { retval = __populate_fs_from_tar(fs, parent_ino, NULL, root, &hdlinks, &file_info, flags, fs_callbacks); goto out; } struct stat st; if (stat(source, &st)) { retval = errno; com_err(__func__, retval, _("while calling stat")); goto out; } if (S_ISREG(st.st_mode)) { retval = __populate_fs_from_tar(fs, parent_ino, source, root, &hdlinks, &file_info, flags, fs_callbacks); goto out; } if ((flags & POPULATE_FS_NO_COPY_XATTRS) == 0) { retval = set_inode_xattr(fs, root, source); if (retval) { com_err(__func__, retval, _("while copying xattrs on root directory")); goto out; } } retval = __populate_fs(fs, parent_ino, source, root, &hdlinks, &file_info, flags, fs_callbacks); out: free(file_info.path); free(hdlinks.hdl); link_append_flag = EXT2FS_LINK_EXPAND; return retval; } errcode_t populate_fs2(ext2_filsys fs, ext2_ino_t parent_ino, const char *source, ext2_ino_t root, struct fs_ops_callbacks *fs_callbacks) { return populate_fs3(fs, parent_ino, source, root, 0, fs_callbacks); } errcode_t populate_fs(ext2_filsys fs, ext2_ino_t parent_ino, const char *source, ext2_ino_t root) { return populate_fs3(fs, parent_ino, source, root, 0, NULL); }