Whamcloud - gitweb
e2fsck: map PROMPT_* values to prompt messages
[tools/e2fsprogs.git] / misc / fuse2fs.c
index 956348f..c595721 100644 (file)
@@ -19,7 +19,7 @@
 # include <linux/fs.h>
 # include <linux/falloc.h>
 # include <linux/xattr.h>
-# define FUSE_PLATFORM_OPTS    ",nonempty,big_writes"
+# define FUSE_PLATFORM_OPTS    ",big_writes"
 # ifdef HAVE_SYS_ACL_H
 #  define TRANSLATE_LINUX_ACLS
 # endif
@@ -118,7 +118,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 +322,9 @@ struct fuse2fs {
        int no_default_opts;
        int panic_on_error;
        int minixdf;
+       int fakeroot;
        int alloc_all_blocks;
+       int norecovery;
        FILE *err_fp;
        unsigned int next_generation;
 };
@@ -623,6 +632,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 +649,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 +662,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 +680,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;
@@ -745,23 +755,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 +774,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);
@@ -863,8 +857,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);
@@ -888,9 +883,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)) {
@@ -1006,7 +999,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) {
@@ -1130,7 +1125,9 @@ 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;
+       ext2fs_set_i_gid_high(inode, ctxt->gid >> 16);
        inode.i_mode = LINUX_S_IFDIR | (mode & ~(S_ISUID | fs->umask)) |
                       parent_sgid;
        inode.i_generation = ff->next_generation++;
@@ -1503,7 +1500,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,
@@ -1899,7 +1898,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;
        }
@@ -1909,7 +1908,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;
@@ -1962,23 +1961,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);
@@ -2356,7 +2358,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)
@@ -2654,12 +2656,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)
@@ -2726,12 +2722,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);
@@ -2918,7 +2908,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;
 
@@ -3126,6 +3118,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);
@@ -3135,7 +3128,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)
@@ -3182,6 +3175,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);
@@ -3191,7 +3185,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;
@@ -3661,8 +3655,10 @@ 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),
 
        FUSE_OPT_KEY("-V",             FUSE2FS_VERSION),
        FUSE_OPT_KEY("--version",      FUSE2FS_VERSION),
@@ -3699,7 +3695,9 @@ 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 norecovery          don't replay the journal (implies ro)\n"
        "    -o fuse2fs_debug       enable fuse2fs debugging\n"
        "\n",
                        outargs->argv[0]);
@@ -3729,7 +3727,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;
@@ -3741,6 +3740,8 @@ int main(int argc, char *argv[])
                exit(1);
        }
 
+       if (fctx.norecovery)
+               fctx.ro = 1;
        if (fctx.ro)
                printf("%s", _("Mounting read-only.\n"));
 
@@ -3759,7 +3760,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;
@@ -3780,20 +3781,19 @@ int main(int argc, char *argv[])
        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_ea_inode(global_fs->super)) {
-               printf(_("%s: fuse2fs does not support ea_inode feature.\n"),
-                      fctx.device);
-               goto out;
-       }
 
        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) {
@@ -3856,10 +3856,19 @@ int main(int argc, char *argv[])
        /* Set up default fuse parameters */
        snprintf(extra_args, BUFSIZ, "-okernel_cache,subtype=ext4,use_ino,"
                 "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;
 
@@ -3875,12 +3884,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;
 }