Whamcloud - gitweb
LU-17808 Revert "Remove explicit #define of _FILE_OFFSET_BITS"
[tools/e2fsprogs.git] / misc / fuse2fs.c
index 9feafd7..084ba28 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"
 
@@ -118,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)
@@ -315,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;
 };
@@ -623,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;
@@ -639,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)
@@ -652,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;
@@ -670,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;
@@ -717,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;
@@ -735,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);
@@ -745,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;
@@ -781,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);
@@ -803,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;
@@ -1005,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) {
@@ -1129,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++;
 
@@ -1231,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--;
@@ -1502,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,
@@ -1541,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;
@@ -1870,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;
@@ -1898,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;
        }
@@ -1908,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;
@@ -1929,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;
@@ -1961,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);
@@ -1996,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;
@@ -2355,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)
@@ -2493,6 +2526,7 @@ out:
 
 static int count_buffer_space(char *name, char *value EXT2FS_ATTR((unused)),
                              size_t value_len EXT2FS_ATTR((unused)),
+                             ext2_ino_t inode_num EXT2FS_ATTR((unused)),
                              void *data)
 {
        unsigned int *x = data;
@@ -2502,12 +2536,15 @@ static int count_buffer_space(char *name, char *value EXT2FS_ATTR((unused)),
 }
 
 static int copy_names(char *name, char *value EXT2FS_ATTR((unused)),
-                     size_t value_len EXT2FS_ATTR((unused)), void *data)
+                     size_t value_len EXT2FS_ATTR((unused)),
+                     ext2_ino_t inode_num 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;
 }
@@ -2748,7 +2785,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;
 
@@ -2758,7 +2799,11 @@ 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;
@@ -2905,7 +2950,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;
 
@@ -2944,6 +2991,7 @@ 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)
 {
@@ -3017,8 +3065,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;
@@ -3113,6 +3166,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);
@@ -3122,7 +3176,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)
@@ -3169,6 +3223,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);
@@ -3178,7 +3233,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;
@@ -3250,7 +3305,12 @@ 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)
@@ -3594,10 +3654,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
@@ -3609,10 +3671,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
@@ -3648,8 +3714,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),
@@ -3686,7 +3755,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]);
@@ -3716,7 +3788,8 @@ int main(int argc, char *argv[])
        errcode_t err;
        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;
@@ -3728,6 +3801,8 @@ int main(int argc, char *argv[])
                exit(1);
        }
 
+       if (fctx.norecovery)
+               fctx.ro = 1;
        if (fctx.ro)
                printf("%s", _("Mounting read-only.\n"));
 
@@ -3762,7 +3837,9 @@ 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));
@@ -3775,7 +3852,11 @@ int main(int argc, char *argv[])
        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) {
@@ -3836,12 +3917,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;
 
@@ -3951,14 +4041,14 @@ no_translation:
 
        /* 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;