Whamcloud - gitweb
Prevent i_dtime from being mistaken for an inode number post-2038 wraparound
[tools/e2fsprogs.git] / misc / fuse2fs.c
index 4412fe3..5927fdf 100644 (file)
@@ -9,7 +9,6 @@
  * %End-Header%
  */
 #define _FILE_OFFSET_BITS 64
-#define FUSE_USE_VERSION 29
 #ifndef _GNU_SOURCE
 #define _GNU_SOURCE
 #endif
 # include <linux/fs.h>
 # include <linux/falloc.h>
 # include <linux/xattr.h>
-# define FUSE_PLATFORM_OPTS    ",nonempty,big_writes"
 # ifdef HAVE_SYS_ACL_H
 #  define TRANSLATE_LINUX_ACLS
 # endif
-#else
-# define FUSE_PLATFORM_OPTS    ""
 #endif
 #ifdef TRANSLATE_LINUX_ACLS
 # include <sys/acl.h>
 #include <inttypes.h>
 #include "ext2fs/ext2fs.h"
 #include "ext2fs/ext2_fs.h"
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+# define FUSE_PLATFORM_OPTS    ""
+#else
+# ifdef __linux__
+#  define FUSE_PLATFORM_OPTS   ",use_ino,big_writes"
+# else
+#  define FUSE_PLATFORM_OPTS   ",use_ino"
+# endif
+#endif
 
 #include "../version.h"
 
@@ -95,6 +100,10 @@ static ext2_filsys global_fs; /* Try not to use this directly */
 
 errcode_t ext2fs_run_ext3_journal(ext2_filsys *fs);
 
+#ifdef CONFIG_JBD_DEBUG                /* Enabled by configure --enable-jbd-debug */
+int journal_enable_debug = -1;
+#endif
+
 /* ACL translation stuff */
 #ifdef TRANSLATE_LINUX_ACLS
 /*
@@ -114,7 +123,14 @@ typedef struct {
 
 typedef struct {
        u_int32_t       a_version;
+#if __GNUC_PREREQ (4, 8)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
        acl_ea_entry    a_entries[0];
+#if __GNUC_PREREQ (4, 8)
+#pragma GCC diagnostic pop
+#endif
 } acl_ea_header;
 
 static inline size_t acl_ea_size(int count)
@@ -311,7 +327,10 @@ struct fuse2fs {
        int no_default_opts;
        int panic_on_error;
        int minixdf;
+       int fakeroot;
        int alloc_all_blocks;
+       int norecovery;
+       unsigned long offset;
        FILE *err_fp;
        unsigned int next_generation;
 };
@@ -619,6 +638,7 @@ static int fs_writeable(ext2_filsys fs)
 static int check_inum_access(ext2_filsys fs, ext2_ino_t ino, mode_t mask)
 {
        struct fuse_context *ctxt = fuse_get_context();
+       struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
        struct ext2_inode inode;
        mode_t perms;
        errcode_t err;
@@ -635,8 +655,8 @@ static int check_inum_access(ext2_filsys fs, ext2_ino_t ino, mode_t mask)
        dbg_printf("access ino=%d mask=e%s%s%s perms=0%o fuid=%d fgid=%d "
                   "uid=%d gid=%d\n", ino,
                   (mask & R_OK ? "r" : ""), (mask & W_OK ? "w" : ""),
-                  (mask & X_OK ? "x" : ""), perms, inode.i_uid, inode.i_gid,
-                  ctxt->uid, ctxt->gid);
+                  (mask & X_OK ? "x" : ""), perms, inode_uid(inode),
+                  inode_gid(inode), ctxt->uid, ctxt->gid);
 
        /* existence check */
        if (mask == 0)
@@ -648,7 +668,7 @@ static int check_inum_access(ext2_filsys fs, ext2_ino_t ino, mode_t mask)
                return -EACCES;
 
        /* Figure out what root's allowed to do */
-       if (ctxt->uid == 0) {
+       if (ff->fakeroot || ctxt->uid == 0) {
                /* Non-file access always ok */
                if (!LINUX_S_ISREG(inode.i_mode))
                        return 0;
@@ -666,14 +686,14 @@ static int check_inum_access(ext2_filsys fs, ext2_ino_t ino, mode_t mask)
        }
 
        /* allow owner, if perms match */
-       if (inode.i_uid == ctxt->uid) {
+       if (inode_uid(inode) == ctxt->uid) {
                if ((mask & (perms >> 6)) == mask)
                        return 0;
                return -EACCES;
        }
 
        /* allow group, if perms match */
-       if (inode.i_gid == ctxt->gid) {
+       if (inode_gid(inode) == ctxt->gid) {
                if ((mask & (perms >> 3)) == mask)
                        return 0;
                return -EACCES;
@@ -713,7 +733,11 @@ static void op_destroy(void *p EXT2FS_ATTR((unused)))
        }
 }
 
-static void *op_init(struct fuse_conn_info *conn)
+static void *op_init(struct fuse_conn_info *conn
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+                       , struct fuse_config *cfg EXT2FS_ATTR((unused))
+#endif
+                       )
 {
        struct fuse_context *ctxt = fuse_get_context();
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
@@ -731,7 +755,7 @@ static void *op_init(struct fuse_conn_info *conn)
 #endif
        if (fs->flags & EXT2_FLAG_RW) {
                fs->super->s_mnt_count++;
-               fs->super->s_mtime = time(NULL);
+               ext2fs_set_tstamp(fs->super, s_mtime, time(NULL));
                fs->super->s_state &= ~EXT2_VALID_FS;
                ext2fs_mark_super_dirty(fs);
                err = ext2fs_flush2(fs, 0);
@@ -741,23 +765,6 @@ static void *op_init(struct fuse_conn_info *conn)
        return ff;
 }
 
-static blkcnt_t blocks_from_inode(ext2_filsys fs,
-                                 struct ext2_inode_large *inode)
-{
-       blkcnt_t b;
-
-       b = inode->i_blocks;
-       if (ext2fs_has_feature_huge_file(fs->super))
-               b += ((long long) inode->osd2.linux2.l_i_blocks_hi) << 32;
-
-       if (!ext2fs_has_feature_huge_file(fs->super) ||
-           !(inode->i_flags & EXT4_HUGE_FILE_FL))
-               b *= fs->blocksize / 512;
-       b *= EXT2FS_CLUSTER_RATIO(fs);
-
-       return b;
-}
-
 static int stat_inode(ext2_filsys fs, ext2_ino_t ino, struct stat *statbuf)
 {
        struct ext2_inode_large inode;
@@ -777,11 +784,12 @@ static int stat_inode(ext2_filsys fs, ext2_ino_t ino, struct stat *statbuf)
        statbuf->st_ino = ino;
        statbuf->st_mode = inode.i_mode;
        statbuf->st_nlink = inode.i_links_count;
-       statbuf->st_uid = inode.i_uid;
-       statbuf->st_gid = inode.i_gid;
+       statbuf->st_uid = inode_uid(inode);
+       statbuf->st_gid = inode_gid(inode);
        statbuf->st_size = EXT2_I_SIZE(&inode);
        statbuf->st_blksize = fs->blocksize;
-       statbuf->st_blocks = blocks_from_inode(fs, &inode);
+       statbuf->st_blocks = ext2fs_get_stat_i_blocks(fs,
+                                               (struct ext2_inode *)&inode);
        EXT4_INODE_GET_XTIME(i_atime, &tv, &inode);
        statbuf->st_atime = tv.tv_sec;
        EXT4_INODE_GET_XTIME(i_mtime, &tv, &inode);
@@ -799,7 +807,11 @@ static int stat_inode(ext2_filsys fs, ext2_ino_t ino, struct stat *statbuf)
        return ret;
 }
 
-static int op_getattr(const char *path, struct stat *statbuf)
+static int op_getattr(const char *path, struct stat *statbuf
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+                       , struct fuse_file_info *fi EXT2FS_ATTR((unused))
+#endif
+                       )
 {
        struct fuse_context *ctxt = fuse_get_context();
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
@@ -859,8 +871,9 @@ static int op_readlink(const char *path, char *buf, size_t len)
        len--;
        if (inode.i_size < len)
                len = inode.i_size;
-       if (ext2fs_inode_data_blocks2(fs, &inode) ||
-           (inode.i_flags & EXT4_INLINE_DATA_FL)) {
+       if (ext2fs_is_fast_symlink(&inode))
+               memcpy(buf, (char *)inode.i_block, len);
+       else {
                /* big/inline symlink */
 
                err = ext2fs_file_open(fs, ino, 0, &file);
@@ -884,9 +897,7 @@ out2:
                        ret = translate_error(fs, ino, err);
                        goto out;
                }
-       } else
-               /* inline symlink */
-               memcpy(buf, (char *)inode.i_block, len);
+       }
        buf[len] = 0;
 
        if (fs_writeable(fs)) {
@@ -906,7 +917,7 @@ static int op_mknod(const char *path, mode_t mode, dev_t dev)
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
        ext2_filsys fs;
        ext2_ino_t parent, child;
-       char *temp_path = strdup(path);
+       char *temp_path;
        errcode_t err;
        char *node_name, a;
        int filetype;
@@ -917,6 +928,7 @@ static int op_mknod(const char *path, mode_t mode, dev_t dev)
        fs = ff->fs;
        dbg_printf("%s: path=%s mode=0%o dev=0x%x\n", __func__, path, mode,
                   (unsigned int)dev);
+       temp_path = strdup(path);
        if (!temp_path) {
                ret = -ENOMEM;
                goto out;
@@ -1001,7 +1013,9 @@ static int op_mknod(const char *path, mode_t mode, dev_t dev)
        inode.i_extra_isize = sizeof(struct ext2_inode_large) -
                EXT2_GOOD_OLD_INODE_SIZE;
        inode.i_uid = ctxt->uid;
+       ext2fs_set_i_uid_high(inode, ctxt->uid >> 16);
        inode.i_gid = ctxt->gid;
+       ext2fs_set_i_gid_high(inode, ctxt->gid >> 16);
 
        err = ext2fs_write_new_inode(fs, child, (struct ext2_inode *)&inode);
        if (err) {
@@ -1033,7 +1047,7 @@ static int op_mkdir(const char *path, mode_t mode)
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
        ext2_filsys fs;
        ext2_ino_t parent, child;
-       char *temp_path = strdup(path);
+       char *temp_path;
        errcode_t err;
        char *node_name, a;
        struct ext2_inode_large inode;
@@ -1045,6 +1059,7 @@ static int op_mkdir(const char *path, mode_t mode)
        FUSE2FS_CHECK_CONTEXT(ff);
        fs = ff->fs;
        dbg_printf("%s: path=%s mode=0%o\n", __func__, path, mode);
+       temp_path = strdup(path);
        if (!temp_path) {
                ret = -ENOMEM;
                goto out;
@@ -1124,8 +1139,10 @@ static int op_mkdir(const char *path, mode_t mode)
        }
 
        inode.i_uid = ctxt->uid;
+       ext2fs_set_i_uid_high(inode, ctxt->uid >> 16);
        inode.i_gid = ctxt->gid;
-       inode.i_mode = LINUX_S_IFDIR | (mode & ~(S_ISUID | fs->umask)) |
+       ext2fs_set_i_gid_high(inode, ctxt->gid >> 16);
+       inode.i_mode = LINUX_S_IFDIR | (mode & ~S_ISUID) |
                       parent_sgid;
        inode.i_generation = ff->next_generation++;
 
@@ -1226,7 +1243,7 @@ static int remove_inode(struct fuse2fs *ff, ext2_ino_t ino)
                return 0; /* XXX: already done? */
        case 1:
                inode.i_links_count--;
-               inode.i_dtime = fs->now ? fs->now : time(0);
+               ext2fs_set_dtime(fs, EXT2_INODE(&inode));
                break;
        default:
                inode.i_links_count--;
@@ -1420,7 +1437,7 @@ static int op_symlink(const char *src, const char *dest)
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
        ext2_filsys fs;
        ext2_ino_t parent, child;
-       char *temp_path = strdup(dest);
+       char *temp_path;
        errcode_t err;
        char *node_name, a;
        struct ext2_inode_large inode;
@@ -1429,6 +1446,7 @@ static int op_symlink(const char *src, const char *dest)
        FUSE2FS_CHECK_CONTEXT(ff);
        fs = ff->fs;
        dbg_printf("%s: symlink %s to %s\n", __func__, src, dest);
+       temp_path = strdup(dest);
        if (!temp_path) {
                ret = -ENOMEM;
                goto out;
@@ -1496,7 +1514,9 @@ static int op_symlink(const char *src, const char *dest)
        }
 
        inode.i_uid = ctxt->uid;
+       ext2fs_set_i_uid_high(inode, ctxt->uid >> 16);
        inode.i_gid = ctxt->gid;
+       ext2fs_set_i_gid_high(inode, ctxt->gid >> 16);
        inode.i_generation = ff->next_generation++;
 
        err = ext2fs_write_inode_full(fs, child, (struct ext2_inode *)&inode,
@@ -1535,7 +1555,11 @@ static int update_dotdot_helper(ext2_ino_t dir EXT2FS_ATTR((unused)),
        return 0;
 }
 
-static int op_rename(const char *from, const char *to)
+static int op_rename(const char *from, const char *to
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+                       , unsigned int flags EXT2FS_ATTR((unused))
+#endif
+                       )
 {
        struct fuse_context *ctxt = fuse_get_context();
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
@@ -1765,7 +1789,7 @@ static int op_link(const char *src, const char *dest)
        struct fuse_context *ctxt = fuse_get_context();
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
        ext2_filsys fs;
-       char *temp_path = strdup(dest);
+       char *temp_path;
        errcode_t err;
        char *node_name, a;
        ext2_ino_t parent, ino;
@@ -1775,6 +1799,7 @@ static int op_link(const char *src, const char *dest)
        FUSE2FS_CHECK_CONTEXT(ff);
        fs = ff->fs;
        dbg_printf("%s: src=%s dest=%s\n", __func__, src, dest);
+       temp_path = strdup(dest);
        if (!temp_path) {
                ret = -ENOMEM;
                goto out;
@@ -1863,7 +1888,11 @@ out:
        return ret;
 }
 
-static int op_chmod(const char *path, mode_t mode)
+static int op_chmod(const char *path, mode_t mode
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+                       , struct fuse_file_info *fi EXT2FS_ATTR((unused))
+#endif
+                       )
 {
        struct fuse_context *ctxt = fuse_get_context();
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
@@ -1891,7 +1920,7 @@ static int op_chmod(const char *path, mode_t mode)
                goto out;
        }
 
-       if (ctxt->uid != 0 && ctxt->uid != inode.i_uid) {
+       if (!ff->fakeroot && ctxt->uid != 0 && ctxt->uid != inode_uid(inode)) {
                ret = -EPERM;
                goto out;
        }
@@ -1901,7 +1930,7 @@ static int op_chmod(const char *path, mode_t mode)
         * of the user's groups, but FUSE only tells us about the primary
         * group.
         */
-       if (ctxt->uid != 0 && ctxt->gid != inode.i_gid)
+       if (!ff->fakeroot && ctxt->uid != 0 && ctxt->gid != inode_gid(inode))
                mode &= ~S_ISGID;
 
        inode.i_mode &= ~0xFFF;
@@ -1922,7 +1951,11 @@ out:
        return ret;
 }
 
-static int op_chown(const char *path, uid_t owner, gid_t group)
+static int op_chown(const char *path, uid_t owner, gid_t group
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+                       , struct fuse_file_info *fi EXT2FS_ATTR((unused))
+#endif
+                       )
 {
        struct fuse_context *ctxt = fuse_get_context();
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
@@ -1954,23 +1987,26 @@ static int op_chown(const char *path, uid_t owner, gid_t group)
        /* FUSE seems to feed us ~0 to mean "don't change" */
        if (owner != (uid_t) ~0) {
                /* Only root gets to change UID. */
-               if (ctxt->uid != 0 &&
-                   !(inode.i_uid == ctxt->uid && owner == ctxt->uid)) {
+               if (!ff->fakeroot && ctxt->uid != 0 &&
+                   !(inode_uid(inode) == ctxt->uid && owner == ctxt->uid)) {
                        ret = -EPERM;
                        goto out;
                }
                inode.i_uid = owner;
+               ext2fs_set_i_uid_high(inode, owner >> 16);
        }
 
        if (group != (gid_t) ~0) {
                /* Only root or the owner get to change GID. */
-               if (ctxt->uid != 0 && inode.i_uid != ctxt->uid) {
+               if (!ff->fakeroot && ctxt->uid != 0 &&
+                   inode_uid(inode) != ctxt->uid) {
                        ret = -EPERM;
                        goto out;
                }
 
                /* XXX: We /should/ check group membership but FUSE */
                inode.i_gid = group;
+               ext2fs_set_i_gid_high(inode, group >> 16);
        }
 
        ret = update_ctime(fs, ino, &inode);
@@ -1989,7 +2025,11 @@ out:
        return ret;
 }
 
-static int op_truncate(const char *path, off_t len)
+static int op_truncate(const char *path, off_t len
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+                       , struct fuse_file_info *fi EXT2FS_ATTR((unused))
+#endif
+                       )
 {
        struct fuse_context *ctxt = fuse_get_context();
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
@@ -2121,7 +2161,7 @@ static int __op_open(struct fuse2fs *ff, const char *path,
                } else
                        goto out;
        }
-       fp->fh = (uint64_t)file;
+       fp->fh = (uintptr_t)file;
 
 out:
        if (ret)
@@ -2148,7 +2188,8 @@ static int op_read(const char *path EXT2FS_ATTR((unused)), char *buf,
 {
        struct fuse_context *ctxt = fuse_get_context();
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
-       struct fuse2fs_file_handle *fh = (struct fuse2fs_file_handle *)fp->fh;
+       struct fuse2fs_file_handle *fh =
+               (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
        ext2_filsys fs;
        ext2_file_t efp;
        errcode_t err;
@@ -2204,7 +2245,8 @@ static int op_write(const char *path EXT2FS_ATTR((unused)),
 {
        struct fuse_context *ctxt = fuse_get_context();
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
-       struct fuse2fs_file_handle *fh = (struct fuse2fs_file_handle *)fp->fh;
+       struct fuse2fs_file_handle *fh =
+               (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
        ext2_filsys fs;
        ext2_file_t efp;
        errcode_t err;
@@ -2275,7 +2317,8 @@ static int op_release(const char *path EXT2FS_ATTR((unused)),
 {
        struct fuse_context *ctxt = fuse_get_context();
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
-       struct fuse2fs_file_handle *fh = (struct fuse2fs_file_handle *)fp->fh;
+       struct fuse2fs_file_handle *fh =
+               (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
        ext2_filsys fs;
        errcode_t err;
        int ret = 0;
@@ -2304,7 +2347,8 @@ static int op_fsync(const char *path EXT2FS_ATTR((unused)),
 {
        struct fuse_context *ctxt = fuse_get_context();
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
-       struct fuse2fs_file_handle *fh = (struct fuse2fs_file_handle *)fp->fh;
+       struct fuse2fs_file_handle *fh =
+               (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
        ext2_filsys fs;
        errcode_t err;
        int ret = 0;
@@ -2344,7 +2388,7 @@ static int op_statfs(const char *path EXT2FS_ATTR((unused)),
                overhead = 0;
        else
                overhead = fs->desc_blocks +
-                          fs->group_desc_count *
+                          (blk64_t)fs->group_desc_count *
                           (fs->inode_blocks_per_group + 2);
        reserved = ext2fs_r_blocks_count(fs->super);
        if (!reserved)
@@ -2494,9 +2538,10 @@ static int copy_names(char *name, char *value EXT2FS_ATTR((unused)),
                      size_t value_len EXT2FS_ATTR((unused)), void *data)
 {
        char **b = data;
+       size_t name_len = strlen(name);
 
-       strncpy(*b, name, strlen(name));
-       *b = *b + strlen(name) + 1;
+       memcpy(*b, name, name_len + 1);
+       *b = *b + name_len + 1;
 
        return 0;
 }
@@ -2529,7 +2574,7 @@ static int op_listxattr(const char *path, char *names, size_t len)
 
        ret = check_inum_access(fs, ino, R_OK);
        if (ret)
-               goto out2;
+               goto out;
 
        err = ext2fs_xattrs_open(fs, ino, &h);
        if (err) {
@@ -2642,12 +2687,6 @@ static int op_setxattr(const char *path EXT2FS_ATTR((unused)),
                goto out3;
        }
 
-       err = ext2fs_xattrs_write(h);
-       if (err) {
-               ret = translate_error(fs, ino, err);
-               goto out3;
-       }
-
        ret = update_ctime(fs, ino, NULL);
 out3:
        if (cvalue != value)
@@ -2714,12 +2753,6 @@ static int op_removexattr(const char *path, const char *key)
                goto out2;
        }
 
-       err = ext2fs_xattrs_write(h);
-       if (err) {
-               ret = translate_error(fs, ino, err);
-               goto out2;
-       }
-
        ret = update_ctime(fs, ino, NULL);
 out2:
        err = ext2fs_xattrs_close(&h);
@@ -2749,7 +2782,11 @@ static int op_readdir_iter(ext2_ino_t dir EXT2FS_ATTR((unused)),
 
        memcpy(namebuf, dirent->name, dirent->name_len & 0xFF);
        namebuf[dirent->name_len & 0xFF] = 0;
-       ret = i->func(i->buf, namebuf, NULL, 0);
+       ret = i->func(i->buf, namebuf, NULL, 0
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+                       , 0
+#endif
+                       );
        if (ret)
                return DIRENT_ABORT;
 
@@ -2759,11 +2796,16 @@ static int op_readdir_iter(ext2_ino_t dir EXT2FS_ATTR((unused)),
 static int op_readdir(const char *path EXT2FS_ATTR((unused)),
                      void *buf, fuse_fill_dir_t fill_func,
                      off_t offset EXT2FS_ATTR((unused)),
-                     struct fuse_file_info *fp)
+                     struct fuse_file_info *fp
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+                       , enum fuse_readdir_flags flags EXT2FS_ATTR((unused))
+#endif
+                       )
 {
        struct fuse_context *ctxt = fuse_get_context();
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
-       struct fuse2fs_file_handle *fh = (struct fuse2fs_file_handle *)fp->fh;
+       struct fuse2fs_file_handle *fh =
+               (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
        ext2_filsys fs;
        errcode_t err;
        struct readdir_iter i;
@@ -2826,7 +2868,7 @@ static int op_create(const char *path, mode_t mode, struct fuse_file_info *fp)
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
        ext2_filsys fs;
        ext2_ino_t parent, child;
-       char *temp_path = strdup(path);
+       char *temp_path;
        errcode_t err;
        char *node_name, a;
        int filetype;
@@ -2836,6 +2878,7 @@ static int op_create(const char *path, mode_t mode, struct fuse_file_info *fp)
        FUSE2FS_CHECK_CONTEXT(ff);
        fs = ff->fs;
        dbg_printf("%s: path=%s mode=0%o\n", __func__, path, mode);
+       temp_path = strdup(path);
        if (!temp_path) {
                ret = -ENOMEM;
                goto out;
@@ -2904,7 +2947,9 @@ static int op_create(const char *path, mode_t mode, struct fuse_file_info *fp)
        inode.i_extra_isize = sizeof(struct ext2_inode_large) -
                EXT2_GOOD_OLD_INODE_SIZE;
        inode.i_uid = ctxt->uid;
+       ext2fs_set_i_uid_high(inode, ctxt->uid >> 16);
        inode.i_gid = ctxt->gid;
+       ext2fs_set_i_gid_high(inode, ctxt->gid >> 16);
        if (ext2fs_has_feature_extents(fs->super)) {
                ext2_extent_handle_t handle;
 
@@ -2943,12 +2988,14 @@ out:
        return ret;
 }
 
+#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
 static int op_ftruncate(const char *path EXT2FS_ATTR((unused)),
                        off_t len, struct fuse_file_info *fp)
 {
        struct fuse_context *ctxt = fuse_get_context();
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
-       struct fuse2fs_file_handle *fh = (struct fuse2fs_file_handle *)fp->fh;
+       struct fuse2fs_file_handle *fh =
+               (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
        ext2_filsys fs;
        ext2_file_t efp;
        errcode_t err;
@@ -3001,7 +3048,8 @@ static int op_fgetattr(const char *path EXT2FS_ATTR((unused)),
        struct fuse_context *ctxt = fuse_get_context();
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
        ext2_filsys fs;
-       struct fuse2fs_file_handle *fh = (struct fuse2fs_file_handle *)fp->fh;
+       struct fuse2fs_file_handle *fh =
+               (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
        int ret = 0;
 
        FUSE2FS_CHECK_CONTEXT(ff);
@@ -3014,8 +3062,13 @@ static int op_fgetattr(const char *path EXT2FS_ATTR((unused)),
 
        return ret;
 }
+#endif /* FUSE_VERSION < FUSE_MAKE_VERSION(3, 0) */
 
-static int op_utimens(const char *path, const struct timespec ctv[2])
+static int op_utimens(const char *path, const struct timespec ctv[2]
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+                       , struct fuse_file_info *fi EXT2FS_ATTR((unused))
+#endif
+                       )
 {
        struct fuse_context *ctxt = fuse_get_context();
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
@@ -3110,6 +3163,7 @@ static int ioctl_setflags(ext2_filsys fs, struct fuse2fs_file_handle *fh,
        int ret;
        __u32 flags = *(__u32 *)data;
        struct fuse_context *ctxt = fuse_get_context();
+       struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
 
        FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
        dbg_printf("%s: ino=%d\n", __func__, fh->ino);
@@ -3119,7 +3173,7 @@ static int ioctl_setflags(ext2_filsys fs, struct fuse2fs_file_handle *fh,
        if (err)
                return translate_error(fs, fh->ino, err);
 
-       if (ctxt->uid != 0 && inode.i_uid != ctxt->uid)
+       if (!ff->fakeroot && ctxt->uid != 0 && inode_uid(inode) != ctxt->uid)
                return -EPERM;
 
        if ((inode.i_flags ^ flags) & ~FUSE2FS_MODIFIABLE_IFLAGS)
@@ -3166,6 +3220,7 @@ static int ioctl_setversion(ext2_filsys fs, struct fuse2fs_file_handle *fh,
        int ret;
        __u32 generation = *(__u32 *)data;
        struct fuse_context *ctxt = fuse_get_context();
+       struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
 
        FUSE2FS_CHECK_MAGIC(fs, fh, FUSE2FS_FILE_MAGIC);
        dbg_printf("%s: ino=%d\n", __func__, fh->ino);
@@ -3175,7 +3230,7 @@ static int ioctl_setversion(ext2_filsys fs, struct fuse2fs_file_handle *fh,
        if (err)
                return translate_error(fs, fh->ino, err);
 
-       if (ctxt->uid != 0 && inode.i_uid != ctxt->uid)
+       if (!ff->fakeroot && ctxt->uid != 0 && inode_uid(inode) != ctxt->uid)
                return -EPERM;
 
        inode.i_generation = generation;
@@ -3247,14 +3302,20 @@ static int ioctl_fitrim(ext2_filsys fs, struct fuse2fs_file_handle *fh,
 #endif /* FITRIM */
 
 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
-static int op_ioctl(const char *path EXT2FS_ATTR((unused)), int cmd,
+static int op_ioctl(const char *path EXT2FS_ATTR((unused)),
+#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0)
+                   unsigned int cmd,
+#else
+                   int cmd,
+#endif
                    void *arg EXT2FS_ATTR((unused)),
                    struct fuse_file_info *fp,
                    unsigned int flags EXT2FS_ATTR((unused)), void *data)
 {
        struct fuse_context *ctxt = fuse_get_context();
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
-       struct fuse2fs_file_handle *fh = (struct fuse2fs_file_handle *)fp->fh;
+       struct fuse2fs_file_handle *fh =
+               (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
        ext2_filsys fs;
        int ret = 0;
 
@@ -3329,7 +3390,8 @@ static int fallocate_helper(struct fuse_file_info *fp, int mode, off_t offset,
 {
        struct fuse_context *ctxt = fuse_get_context();
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
-       struct fuse2fs_file_handle *fh = (struct fuse2fs_file_handle *)fp->fh;
+       struct fuse2fs_file_handle *fh =
+               (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
        ext2_filsys fs;
        struct ext2_inode_large inode;
        blk64_t start, end;
@@ -3464,7 +3526,8 @@ static int punch_helper(struct fuse_file_info *fp, int mode, off_t offset,
 {
        struct fuse_context *ctxt = fuse_get_context();
        struct fuse2fs *ff = (struct fuse2fs *)ctxt->private_data;
-       struct fuse2fs_file_handle *fh = (struct fuse2fs_file_handle *)fp->fh;
+       struct fuse2fs_file_handle *fh =
+               (struct fuse2fs_file_handle *)(uintptr_t)fp->fh;
        ext2_filsys fs;
        struct ext2_inode_large inode;
        blk64_t start, end;
@@ -3588,10 +3651,12 @@ static struct fuse_operations fs_ops = {
        .fsyncdir = op_fsync,
        .access = op_access,
        .create = op_create,
+#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
        .ftruncate = op_ftruncate,
        .fgetattr = op_fgetattr,
+#endif
        .utimens = op_utimens,
-#if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
+#if (FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)) && (FUSE_VERSION < FUSE_MAKE_VERSION(3, 0))
 # if defined(UTIME_NOW) || defined(UTIME_OMIT)
        .flag_utime_omit_ok = 1,
 # endif
@@ -3603,10 +3668,14 @@ static struct fuse_operations fs_ops = {
 #endif
 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 8)
        .ioctl = op_ioctl,
+#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
        .flag_nullpath_ok = 1,
 #endif
+#endif
 #if FUSE_VERSION >= FUSE_MAKE_VERSION(2, 9)
+#if FUSE_VERSION < FUSE_MAKE_VERSION(3, 0)
        .flag_nopath = 1,
+#endif
 # ifdef SUPPORT_FALLOCATE
        .fallocate = op_fallocate,
 # endif
@@ -3642,8 +3711,11 @@ static struct fuse_opt fuse2fs_opts[] = {
        FUSE2FS_OPT("ro",               ro,                     1),
        FUSE2FS_OPT("errors=panic",     panic_on_error,         1),
        FUSE2FS_OPT("minixdf",          minixdf,                1),
+       FUSE2FS_OPT("fakeroot",         fakeroot,               1),
        FUSE2FS_OPT("fuse2fs_debug",    debug,                  1),
        FUSE2FS_OPT("no_default_opts",  no_default_opts,        1),
+       FUSE2FS_OPT("norecovery",       norecovery,             1),
+       FUSE2FS_OPT("offset=%lu",       offset,         0),
 
        FUSE_OPT_KEY("-V",             FUSE2FS_VERSION),
        FUSE_OPT_KEY("--version",      FUSE2FS_VERSION),
@@ -3680,7 +3752,10 @@ static int fuse2fs_opt_proc(void *data, const char *arg,
        "    -o ro                  read-only mount\n"
        "    -o errors=panic        dump core on error\n"
        "    -o minixdf             minix-style df\n"
+       "    -o fakeroot            pretend to be root for permission checks\n"
        "    -o no_default_opts     do not include default fuse options\n"
+       "    -o offset=<bytes>      similar to mount -o offset=<bytes>, mount the partition starting at <bytes>\n"
+       "    -o norecovery          don't replay the journal (implies ro)\n"
        "    -o fuse2fs_debug       enable fuse2fs debugging\n"
        "\n",
                        outargs->argv[0]);
@@ -3708,10 +3783,10 @@ int main(int argc, char *argv[])
        struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
        struct fuse2fs fctx;
        errcode_t err;
-       char *tok, *arg, *logfile;
-       int i;
+       char *logfile;
        char extra_args[BUFSIZ];
-       int ret = 0, flags = EXT2_FLAG_64BITS | EXT2_FLAG_EXCLUSIVE;
+       int ret = 0;
+       int flags = EXT2_FLAG_64BITS | EXT2_FLAG_THREADS | EXT2_FLAG_EXCLUSIVE;
 
        memset(&fctx, 0, sizeof(fctx));
        fctx.magic = FUSE2FS_MAGIC;
@@ -3723,6 +3798,8 @@ int main(int argc, char *argv[])
                exit(1);
        }
 
+       if (fctx.norecovery)
+               fctx.ro = 1;
        if (fctx.ro)
                printf("%s", _("Mounting read-only.\n"));
 
@@ -3741,7 +3818,7 @@ int main(int argc, char *argv[])
                fctx.err_fp = fopen(logfile, "a");
                if (!fctx.err_fp) {
                        perror(logfile);
-                       goto out_nofs;
+                       goto out;
                }
        } else
                fctx.err_fp = stderr;
@@ -3757,19 +3834,26 @@ int main(int argc, char *argv[])
        ret = 2;
        if (!fctx.ro)
                flags |= EXT2_FLAG_RW;
-       err = ext2fs_open2(fctx.device, NULL, flags, 0, 0, unix_io_manager,
+       char options[50];
+       sprintf(options, "offset=%lu", fctx.offset);
+       err = ext2fs_open2(fctx.device, options, flags, 0, 0, unix_io_manager,
                           &global_fs);
        if (err) {
                printf(_("%s: %s.\n"), fctx.device, error_message(err));
                printf(_("Please run e2fsck -fy %s.\n"), fctx.device);
-               goto out_nofs;
+               goto out;
        }
        fctx.fs = global_fs;
        global_fs->priv_data = &fctx;
 
        ret = 3;
+
        if (ext2fs_has_feature_journal_needs_recovery(global_fs->super)) {
-               if (!fctx.ro) {
+               if (fctx.norecovery) {
+                       printf(_("%s: mounting read-only without "
+                                "recovering journal\n"),
+                              fctx.device);
+               } else if (!fctx.ro) {
                        printf(_("%s: recovering journal\n"), fctx.device);
                        err = ext2fs_run_ext3_journal(&global_fs);
                        if (err) {
@@ -3812,8 +3896,8 @@ int main(int argc, char *argv[])
                printf("%s", _("Warning: Maximal mount count reached, running "
                       "e2fsck is recommended.\n"));
        if (global_fs->super->s_checkinterval > 0 &&
-           global_fs->super->s_lastcheck +
-           global_fs->super->s_checkinterval <= time(0))
+           (time_t) (global_fs->super->s_lastcheck +
+                     global_fs->super->s_checkinterval) <= time(0))
                printf("%s", _("Warning: Check time reached; running e2fsck "
                       "is recommended.\n"));
        if (global_fs->super->s_last_orphan)
@@ -3830,12 +3914,21 @@ int main(int argc, char *argv[])
        get_random_bytes(&fctx.next_generation, sizeof(unsigned int));
 
        /* Set up default fuse parameters */
-       snprintf(extra_args, BUFSIZ, "-okernel_cache,subtype=ext4,use_ino,"
+       snprintf(extra_args, BUFSIZ, "-okernel_cache,subtype=ext4,"
                 "fsname=%s,attr_timeout=0" FUSE_PLATFORM_OPTS,
-                argv[1]);
+                fctx.device);
        if (fctx.no_default_opts == 0)
                fuse_opt_add_arg(&args, extra_args);
 
+       if (fctx.fakeroot) {
+#ifdef HAVE_MOUNT_NODEV
+               fuse_opt_add_arg(&args,"-onodev");
+#endif
+#ifdef HAVE_MOUNT_NOSUID
+               fuse_opt_add_arg(&args,"-onosuid");
+#endif
+       }
+
        if (fctx.debug) {
                int     i;
 
@@ -3851,12 +3944,12 @@ int main(int argc, char *argv[])
 
        ret = 0;
 out:
-       err = ext2fs_close(global_fs);
-       if (err)
-               com_err(argv[0], err, "while closing fs");
-       global_fs = NULL;
-out_nofs:
-
+       if (global_fs) {
+               err = ext2fs_close(global_fs);
+               if (err)
+                       com_err(argv[0], err, "while closing fs");
+               global_fs = NULL;
+       }
        return ret;
 }
 
@@ -3888,6 +3981,7 @@ static int __translate_error(ext2_filsys fs, errcode_t err, ext2_ino_t ino,
                break;
        case EXT2_ET_DIR_NO_SPACE:
                is_err = 1;
+               /* fallthrough */
        case EXT2_ET_TOOSMALL:
        case EXT2_ET_BLOCK_ALLOC_FAIL:
        case EXT2_ET_INODE_ALLOC_FAIL:
@@ -3934,24 +4028,24 @@ no_translation:
 
        if (ino)
                fprintf(ff->err_fp, "FUSE2FS (%s): %s (inode #%d) at %s:%d.\n",
-                       fs && fs->device_name ? fs->device_name : "???",
+                       fs->device_name ? fs->device_name : "???",
                        error_message(err), ino, file, line);
        else
                fprintf(ff->err_fp, "FUSE2FS (%s): %s at %s:%d.\n",
-                       fs && fs->device_name ? fs->device_name : "???",
+                       fs->device_name ? fs->device_name : "???",
                        error_message(err), file, line);
        fflush(ff->err_fp);
 
        /* Make a note in the error log */
        get_now(&now);
-       fs->super->s_last_error_time = now.tv_sec;
+       ext2fs_set_tstamp(fs->super, s_last_error_time, now.tv_sec);
        fs->super->s_last_error_ino = ino;
        fs->super->s_last_error_line = line;
        fs->super->s_last_error_block = err; /* Yeah... */
        strncpy((char *)fs->super->s_last_error_func, file,
                sizeof(fs->super->s_last_error_func));
-       if (fs->super->s_first_error_time == 0) {
-               fs->super->s_first_error_time = now.tv_sec;
+       if (ext2fs_get_tstamp(fs->super, s_first_error_time) == 0) {
+               ext2fs_set_tstamp(fs->super, s_first_error_time, now.tv_sec);
                fs->super->s_first_error_ino = ino;
                fs->super->s_first_error_line = line;
                fs->super->s_first_error_block = err;