From: Theodore Ts'o Date: Sat, 26 Jul 2014 19:57:42 +0000 (-0400) Subject: Merge branch 'maint' into next X-Git-Tag: v1.43-WIP-2015-05-18~268^2~12 X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fe2fsprogs.git;a=commitdiff_plain;h=22302aa32060870767c271a6c8dd0219fffeae6b Merge branch 'maint' into next Conflicts: debugfs/debugfs.c e2fsck/pass1.c --- 22302aa32060870767c271a6c8dd0219fffeae6b diff --cc e2fsck/pass1.c index 10ffe3946,647d91b48..e1e8662d1 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@@ -567,40 -540,42 +566,76 @@@ void e2fsck_setup_tdb_icount(e2fsck_t c *ret = 0; } +static errcode_t recheck_bad_inode_checksum(ext2_filsys fs, ext2_ino_t ino, + e2fsck_t ctx, + struct problem_context *pctx) +{ + errcode_t retval; + struct ext2_inode_large inode; + + /* + * Reread inode. If we don't see checksum error, then this inode + * has been fixed elsewhere. + */ + retval = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode, + sizeof(inode)); + if (retval && retval != EXT2_ET_INODE_CSUM_INVALID) + return retval; + if (!retval) + return 0; + + /* + * Checksum still doesn't match. That implies that the inode passes + * all the sanity checks, so maybe the checksum is simply corrupt. + * See if the user will go for fixing that. + */ + if (!fix_problem(ctx, PR_1_INODE_ONLY_CSUM_INVALID, pctx)) + return 0; + + retval = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode, + sizeof(inode)); + if (retval) + return retval; + + return 0; +} + + static void reserve_block_for_root_repair(e2fsck_t ctx) + { + blk64_t blk = 0; + errcode_t err; + ext2_filsys fs = ctx->fs; + + ctx->root_repair_block = 0; + if (ext2fs_test_inode_bitmap2(ctx->inode_used_map, EXT2_ROOT_INO)) + return; + + err = ext2fs_new_block2(fs, 0, ctx->block_found_map, &blk); + if (err) + return; + ext2fs_mark_block_bitmap2(ctx->block_found_map, blk); + ctx->root_repair_block = blk; + } + + static void reserve_block_for_lnf_repair(e2fsck_t ctx) + { + blk64_t blk = 0; + errcode_t err; + ext2_filsys fs = ctx->fs; + static const char name[] = "lost+found"; + ext2_ino_t ino; + + ctx->lnf_repair_block = 0; + if (!ext2fs_lookup(fs, EXT2_ROOT_INO, name, sizeof(name)-1, 0, &ino)) + return; + + err = ext2fs_new_block2(fs, 0, ctx->block_found_map, &blk); + if (err) + return; + ext2fs_mark_block_bitmap2(ctx->block_found_map, blk); + ctx->lnf_repair_block = blk; + } + void e2fsck_pass1(e2fsck_t ctx) { int i; diff --cc misc/create_inode.c index c9c99b596,000000000..92086d275 mode 100644,000000..100644 --- a/misc/create_inode.c +++ b/misc/create_inode.c @@@ -1,668 -1,0 +1,673 @@@ +/* + * create_inode.c --- create an inode + * + * Copyright (C) 2014 Robert Yang + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU library + * General Public License, version 2. + * %End-Header% + */ + +#include +#include +#include /* for PATH_MAX */ + +#include "create_inode.h" + +#if __STDC_VERSION__ < 199901L +# if __GNUC__ >= 2 +# define __func__ __FUNCTION__ +# else +# define __func__ "" +# endif +#endif + +/* 64KiB is the minimium blksize to best minimize system call overhead. */ +#ifndef IO_BUFSIZE +#define IO_BUFSIZE 64*1024 +#endif + +/* Block size for `st_blocks' */ +#ifndef S_BLKSIZE +#define S_BLKSIZE 512 +#endif + +/* Link an inode number to a directory */ +static 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, inode.i_flags); + if (retval == EXT2_ET_DIR_NO_SPACE) { + retval = ext2fs_expand_dir(fs, parent_ino); + if (retval) { + com_err(__func__, retval, "while expanding directory"); + return retval; + } + retval = ext2fs_link(fs, parent_ino, name, ino, inode.i_flags); + } + 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; +} + +/* Fill the uid, gid, mode and time for the inode */ +static void fill_inode(struct ext2_inode *inode, struct stat *st) +{ + if (st != NULL) { + inode->i_uid = st->st_uid; + inode->i_gid = st->st_gid; + inode->i_mode |= st->st_mode; + inode->i_atime = st->st_atime; + inode->i_mtime = st->st_mtime; + inode->i_ctime = st->st_ctime; + } +} + +/* Set the uid, gid, mode and time for the inode */ +static errcode_t set_inode_extra(ext2_filsys fs, ext2_ino_t cwd, + ext2_ino_t ino, 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; + } + + fill_inode(&inode, st); + + retval = ext2fs_write_inode(fs, ino, &inode); + if (retval) + com_err(__func__, retval, "while writing inode %u", ino); + return retval; +} + +/* 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, + struct stat *st) +{ + ext2_ino_t ino; + errcode_t retval; + struct ext2_inode inode; + unsigned long devmajor, devminor, mode; + int filetype; + + switch(st->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; + case S_IFSOCK: + mode = LINUX_S_IFSOCK; + filetype = EXT2_FT_SOCK; + break; + default: + return EXT2_ET_INVALID_ARGUMENT; + } + + if (!(fs->flags & EXT2_FLAG_RW)) { + com_err(__func__, 0, "Filesystem opened read/only"); + return EROFS; + } + retval = ext2fs_new_inode(fs, cwd, 010755, 0, &ino); + if (retval) { + com_err(__func__, retval, 0); + return retval; + } + +#ifdef DEBUGFS + printf("Allocated inode: %u\n", ino); +#endif + retval = ext2fs_link(fs, cwd, name, ino, filetype); + if (retval == EXT2_ET_DIR_NO_SPACE) { + retval = ext2fs_expand_dir(fs, cwd); + if (retval) { + com_err(__func__, retval, "while expanding directory"); + return retval; + } + retval = ext2fs_link(fs, cwd, name, ino, filetype); + } + if (retval) { + com_err(name, retval, 0); + 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; + inode.i_atime = inode.i_ctime = inode.i_mtime = + fs->now ? fs->now : time(0); + + if (filetype != S_IFIFO) { + devmajor = major(st->st_rdev); + devminor = minor(st->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 creating inode %u", ino); + + return retval; +} + +/* 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; + struct ext2_inode inode; + struct stat st; + + 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; + +try_again: + 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; + } + goto try_again; + } + if (retval) + com_err("ext2fs_symlink", retval, 0); + return retval; +} + +/* Make a directory in the fs */ +errcode_t do_mkdir_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name, + struct stat *st, ext2_ino_t root) +{ + char *cp; + ext2_ino_t parent_ino, ino; + errcode_t retval; + struct ext2_inode inode; + + + 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; + +try_again: + retval = ext2fs_mkdir(fs, parent_ino, 0, name); + if (retval == EXT2_ET_DIR_NO_SPACE) { + retval = ext2fs_expand_dir(fs, parent_ino); + if (retval) { + com_err(__func__, retval, "while expanding directory"); + return retval; + } + goto try_again; + } + if (retval) + com_err("ext2fs_mkdir", retval, 0); + return retval; +} + +static errcode_t copy_file(ext2_filsys fs, int fd, ext2_ino_t newfile, + int bufsize, int make_holes) +{ + ext2_file_t e2_file; + errcode_t retval, close_ret; + int got; + unsigned int written; + char *buf; + char *ptr; + char *zero_buf; + int cmp; + + retval = ext2fs_file_open(fs, newfile, + EXT2_FILE_WRITE, &e2_file); + if (retval) + return retval; + + retval = ext2fs_get_mem(bufsize, &buf); + if (retval) { + com_err("copy_file", retval, "can't allocate buffer\n"); + goto out_close; + } + + /* This is used for checking whether the whole block is zero */ + retval = ext2fs_get_memzero(bufsize, &zero_buf); + if (retval) { + com_err("copy_file", retval, "can't allocate zero buffer\n"); + goto out_free_buf; + } + + while (1) { + got = read(fd, buf, bufsize); + if (got == 0) + break; + if (got < 0) { + retval = errno; + goto fail; + } + ptr = buf; + + /* Sparse copy */ + if (make_holes) { + /* Check whether all is zero */ + cmp = memcmp(ptr, zero_buf, got); + if (cmp == 0) { + /* The whole block is zero, make a hole */ + retval = ext2fs_file_lseek(e2_file, got, + EXT2_SEEK_CUR, + NULL); + if (retval) + goto fail; + got = 0; + } + } + + /* Normal copy */ + while (got > 0) { + retval = ext2fs_file_write(e2_file, ptr, + got, &written); + if (retval) + goto fail; + + got -= written; + ptr += written; + } + } + +fail: + ext2fs_free_mem(&zero_buf); +out_free_buf: + ext2fs_free_mem(&buf); +out_close: + close_ret = ext2fs_file_close(e2_file); + if (retval == 0) + retval = close_ret; + return retval; +} + +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, ext2_ino_t root) +{ + int fd; + struct stat statbuf; + ext2_ino_t newfile; + errcode_t retval; + struct ext2_inode inode; + int bufsize = IO_BUFSIZE; + int make_holes = 0; + + fd = ext2fs_open_file(src, O_RDONLY, 0); + if (fd < 0) { + com_err(src, errno, 0); + return errno; + } + if (fstat(fd, &statbuf) < 0) { + com_err(src, errno, 0); + close(fd); + return errno; + } + + retval = ext2fs_namei(fs, root, cwd, dest, &newfile); + if (retval == 0) { + close(fd); + return EXT2_ET_FILE_EXISTS; + } + + retval = ext2fs_new_inode(fs, cwd, 010755, 0, &newfile); + if (retval) { + com_err(__func__, retval, 0); + close(fd); + return retval; + } +#ifdef DEBUGFS + printf("Allocated inode: %u\n", newfile); +#endif + retval = ext2fs_link(fs, cwd, dest, newfile, + EXT2_FT_REG_FILE); + if (retval == EXT2_ET_DIR_NO_SPACE) { + retval = ext2fs_expand_dir(fs, cwd); + if (retval) { + com_err(__func__, retval, "while expanding directory"); + close(fd); + return retval; + } + retval = ext2fs_link(fs, cwd, dest, newfile, + EXT2_FT_REG_FILE); + } + if (retval) { + com_err(dest, retval, 0); + close(fd); + return errno; + } + 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 & ~LINUX_S_IFMT) | LINUX_S_IFREG; + inode.i_atime = inode.i_ctime = inode.i_mtime = + fs->now ? fs->now : time(0); + inode.i_links_count = 1; - inode.i_size = statbuf.st_size; ++ retval = ext2fs_inode_size_set(fs, &inode, statbuf.st_size); ++ if (retval) { ++ com_err(dest, retval, 0); ++ close(fd); ++ return retval; ++ } + if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, + EXT4_FEATURE_INCOMPAT_INLINE_DATA)) { + inode.i_flags |= EXT4_INLINE_DATA_FL; + } else if (fs->super->s_feature_incompat & + EXT3_FEATURE_INCOMPAT_EXTENTS) { + int i; + struct ext3_extent_header *eh; + + eh = (struct ext3_extent_header *) &inode.i_block[0]; + eh->eh_depth = 0; + eh->eh_entries = 0; + eh->eh_magic = ext2fs_cpu_to_le16(EXT3_EXT_MAGIC); + i = (sizeof(inode.i_block) - sizeof(*eh)) / + sizeof(struct ext3_extent); + eh->eh_max = ext2fs_cpu_to_le16(i); + inode.i_flags |= EXT4_EXTENTS_FL; + } + + retval = ext2fs_write_new_inode(fs, newfile, &inode); + if (retval) { + com_err(__func__, retval, "while creating inode %u", newfile); + close(fd); + return retval; + } + if (inode.i_flags & EXT4_INLINE_DATA_FL) { + retval = ext2fs_inline_data_init(fs, newfile); + if (retval) { + com_err("copy_file", retval, 0); + close(fd); + return retval; + } + } + if (LINUX_S_ISREG(inode.i_mode)) { + if (statbuf.st_blocks < statbuf.st_size / S_BLKSIZE) { + make_holes = 1; + /* + * Use I/O blocksize as buffer size when + * copying sparse files. + */ + bufsize = statbuf.st_blksize; + } + retval = copy_file(fs, fd, newfile, bufsize, make_holes); + if (retval) + com_err("copy_file", retval, 0); + } + close(fd); + + return retval; +} + +/* Copy files from source_dir to fs */ +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) +{ + const char *name; + DIR *dh; + struct dirent *dent; + struct stat st; + char ln_target[PATH_MAX]; + unsigned int save_inode; + ext2_ino_t ino; + errcode_t retval = 0; + int read_cnt; + int hdlink; + + if (chdir(source_dir) < 0) { + com_err(__func__, errno, + _("while changing working directory to \"%s\""), + source_dir); + return errno; + } + + if (!(dh = opendir("."))) { + com_err(__func__, errno, + _("while opening directory \"%s\""), source_dir); + return errno; + } + + while ((dent = readdir(dh))) { + if ((!strcmp(dent->d_name, ".")) || + (!strcmp(dent->d_name, ".."))) + continue; + if (lstat(dent->d_name, &st)) { + com_err(__func__, errno, _("while lstat \"%s\""), + dent->d_name); + goto out; + } + name = dent->d_name; + + /* 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; + } + + switch(st.st_mode & S_IFMT) { + case S_IFCHR: + case S_IFBLK: + case S_IFIFO: + case S_IFSOCK: + retval = do_mknod_internal(fs, parent_ino, name, &st); + if (retval) { + com_err(__func__, retval, + _("while creating special file " + "\"%s\""), name); + goto out; + } + break; + case S_IFLNK: + read_cnt = readlink(name, ln_target, + sizeof(ln_target) - 1); + if (read_cnt == -1) { + com_err(__func__, errno, + _("while trying to readlink \"%s\""), + name); + retval = errno; + goto out; + } + ln_target[read_cnt] = '\0'; + retval = do_symlink_internal(fs, parent_ino, name, + ln_target, root); + if (retval) { + com_err(__func__, retval, + _("while writing symlink\"%s\""), + name); + goto out; + } + break; + case S_IFREG: + retval = do_write_internal(fs, parent_ino, name, name, + root); + if (retval) { + com_err(__func__, retval, + _("while writing file \"%s\""), name); + goto out; + } + break; + case S_IFDIR: + retval = do_mkdir_internal(fs, parent_ino, name, &st, + root); + if (retval) { + com_err(__func__, retval, + _("while making dir \"%s\""), name); + goto out; + } + 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); + if (retval) { + com_err(__func__, retval, + _("while adding dir \"%s\""), name); + goto out; + } + if (chdir("..")) { + com_err(__func__, errno, _("during cd ..")); + retval = errno; + 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, 0); + goto out; + } + + retval = set_inode_extra(fs, parent_ino, ino, &st); + if (retval) { + com_err(__func__, retval, + _("while setting inode for \"%s\""), name); + 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) { + com_err(name, errno, + _("Not enough memory")); + retval = EXT2_ET_NO_MEMORY; + 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++; + } + } + +out: + closedir(dh); + return retval; +} + +errcode_t populate_fs(ext2_filsys fs, ext2_ino_t parent_ino, + const char *source_dir, ext2_ino_t root) +{ + struct hdlinks_s hdlinks; + errcode_t retval; + + hdlinks.count = 0; + hdlinks.size = HDLINK_CNT; + hdlinks.hdl = realloc(NULL, hdlinks.size * sizeof(struct hdlink_s)); + if (hdlinks.hdl == NULL) { + com_err(__func__, errno, "Not enough memory"); + return errno; + } + + retval = __populate_fs(fs, parent_ino, source_dir, root, &hdlinks); + + free(hdlinks.hdl); + return retval; +}