Whamcloud - gitweb
Merge branch 'maint' into next
[tools/e2fsprogs.git] / misc / tune2fs.c
index 69ee578..9f0d437 100644 (file)
@@ -52,6 +52,9 @@ extern int optind;
 #include <sys/types.h>
 #include <libgen.h>
 #include <limits.h>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
 
 #include "ext2fs/ext2_fs.h"
 #include "ext2fs/ext2fs.h"
@@ -59,6 +62,7 @@ extern int optind;
 #include "et/com_err.h"
 #include "support/plausible.h"
 #include "support/quotaio.h"
+#include "support/devname.h"
 #include "uuid/uuid.h"
 #include "e2p/e2p.h"
 #include "util.h"
@@ -70,11 +74,34 @@ extern int optind;
 #define QOPT_ENABLE    (1)
 #define QOPT_DISABLE   (-1)
 
+#ifndef FS_IOC_SETFSLABEL
+#define FSLABEL_MAX 256
+#define FS_IOC_SETFSLABEL      _IOW(0x94, 50, char[FSLABEL_MAX])
+#endif
+
+#ifndef FS_IOC_GETFSLABEL
+#define FS_IOC_GETFSLABEL      _IOR(0x94, 49, char[FSLABEL_MAX])
+#endif
+
+struct fsuuid {
+       __u32   fsu_len;
+       __u32   fsu_flags;
+       __u8    fsu_uuid[];
+};
+
+#ifndef EXT4_IOC_GETFSUUID
+#define EXT4_IOC_GETFSUUID     _IOR('f', 44, struct fsuuid)
+#endif
+
+#ifndef EXT4_IOC_SETFSUUID
+#define EXT4_IOC_SETFSUUID     _IOW('f', 44, struct fsuuid)
+#endif
+
 extern int ask_yn(const char *string, int def);
 
 const char *program_name = "tune2fs";
 char *device_name;
-char *new_label, *new_last_mounted, *new_UUID;
+char *new_label, *new_last_mounted, *requested_uuid;
 char *io_options;
 static int c_flag, C_flag, e_flag, f_flag, g_flag, i_flag, l_flag, L_flag;
 static int m_flag, M_flag, Q_flag, r_flag, s_flag = -1, u_flag, U_flag, T_flag;
@@ -101,10 +128,12 @@ static int rewrite_checksums;
 static int feature_64bit;
 static int fsck_requested;
 static char *undo_file;
+int enabling_casefold;
 
-int journal_size, journal_flags;
+int journal_size, journal_fc_size, journal_flags;
 char *journal_device;
 static blk64_t journal_location = ~0LL;
+static e2_blkcnt_t orphan_file_blocks;
 
 static struct list_head blk_move_list;
 
@@ -151,7 +180,8 @@ static __u32 ok_features[3] = {
        EXT3_FEATURE_COMPAT_HAS_JOURNAL |
                EXT2_FEATURE_COMPAT_DIR_INDEX |
                EXT4_FEATURE_COMPAT_FAST_COMMIT |
-               EXT4_FEATURE_COMPAT_STABLE_INODES,
+               EXT4_FEATURE_COMPAT_STABLE_INODES |
+               EXT4_FEATURE_COMPAT_ORPHAN_FILE,
        /* Incompat */
        EXT2_FEATURE_INCOMPAT_FILETYPE |
                EXT3_FEATURE_INCOMPAT_EXTENTS |
@@ -161,7 +191,8 @@ static __u32 ok_features[3] = {
                EXT4_FEATURE_INCOMPAT_64BIT |
                EXT4_FEATURE_INCOMPAT_ENCRYPT |
                EXT4_FEATURE_INCOMPAT_CSUM_SEED |
-               EXT4_FEATURE_INCOMPAT_LARGEDIR,
+               EXT4_FEATURE_INCOMPAT_LARGEDIR |
+               EXT4_FEATURE_INCOMPAT_CASEFOLD,
        /* R/O compat */
        EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
                EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
@@ -181,13 +212,15 @@ static __u32 clear_ok_features[3] = {
        EXT3_FEATURE_COMPAT_HAS_JOURNAL |
                EXT2_FEATURE_COMPAT_RESIZE_INODE |
                EXT2_FEATURE_COMPAT_DIR_INDEX |
-               EXT4_FEATURE_COMPAT_FAST_COMMIT,
+               EXT4_FEATURE_COMPAT_FAST_COMMIT |
+               EXT4_FEATURE_COMPAT_ORPHAN_FILE,
        /* Incompat */
        EXT2_FEATURE_INCOMPAT_FILETYPE |
                EXT4_FEATURE_INCOMPAT_FLEX_BG |
                EXT4_FEATURE_INCOMPAT_MMP |
                EXT4_FEATURE_INCOMPAT_64BIT |
-               EXT4_FEATURE_INCOMPAT_CSUM_SEED,
+               EXT4_FEATURE_INCOMPAT_CSUM_SEED |
+               EXT4_FEATURE_INCOMPAT_CASEFOLD,
        /* R/O compat */
        EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
                EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
@@ -391,6 +424,8 @@ static errcode_t remove_journal_inode(ext2_filsys fs)
                                _("while clearing journal inode"));
                        return retval;
                }
+               fs->super->s_overhead_clusters -=
+                       EXT2FS_NUM_B2C(fs, EXT2_I_SIZE(&inode) / fs->blocksize);
                memset(&inode, 0, sizeof(inode));
                ext2fs_mark_bb_dirty(fs);
                fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
@@ -1001,6 +1036,41 @@ out:
        return retval;
 }
 
+static int has_casefold_inode(ext2_filsys fs)
+{
+       int length = EXT2_INODE_SIZE(fs->super);
+       struct ext2_inode *inode = NULL;
+       ext2_inode_scan scan;
+       errcode_t       retval;
+       ext2_ino_t      ino;
+       int found_casefold = 0;
+
+       retval = ext2fs_get_mem(length, &inode);
+       if (retval)
+               fatal_err(retval, "while allocating memory");
+
+       retval = ext2fs_open_inode_scan(fs, 0, &scan);
+       if (retval)
+               fatal_err(retval, "while opening inode scan");
+
+       do {
+               retval = ext2fs_get_next_inode_full(scan, &ino, inode, length);
+               if (retval)
+                       fatal_err(retval, "while getting next inode");
+               if (!ino)
+                       break;
+
+               if(inode->i_flags & EXT4_CASEFOLD_FL) {
+                       found_casefold = 1;
+                       break;
+               }
+       } while(1);
+
+       ext2fs_free_mem(&inode);
+       ext2fs_close_inode_scan(scan);
+       return found_casefold;
+}
+
 static errcode_t disable_uninit_bg(ext2_filsys fs, __u32 csum_feature_flag)
 {
        struct ext2_group_desc *gd;
@@ -1141,6 +1211,56 @@ static int update_feature_set(ext2_filsys fs, char *features)
                }
        }
 
+       if (FEATURE_OFF(E2P_FEATURE_COMPAT, EXT4_FEATURE_COMPAT_ORPHAN_FILE)) {
+               ext2_ino_t ino;
+
+               if (mount_flags & EXT2_MF_MOUNTED) {
+                       fputs(_("The orphan_file feature may only be cleared "
+                               "when the filesystem is unmounted.\n"), stderr);
+                       return 1;
+               }
+               if (ext2fs_has_feature_orphan_present(sb) && f_flag < 2) {
+                       fputs(_("The orphan_present feature is set. Please "
+                               "run e2fsck before clearing orphan_file "
+                               "feature.\n"),
+                             stderr);
+                       return 1;
+               }
+               err = ext2fs_read_bitmaps(fs);
+               if (err) {
+                       com_err(program_name, err, "%s",
+                               _("while loading bitmaps"));
+                       return 1;
+               }
+               err = ext2fs_truncate_orphan_file(fs);
+               if (err) {
+                       com_err(program_name, err,
+                               _("\n\twhile trying to delete orphan file\n"));
+                       return 1;
+               }
+               ino = sb->s_orphan_file_inum;
+               sb->s_orphan_file_inum = 0;
+               ext2fs_inode_alloc_stats2(fs, ino, -1, 0);
+               ext2fs_clear_feature_orphan_file(sb);
+               ext2fs_clear_feature_orphan_present(sb);
+               ext2fs_mark_super_dirty(fs);
+       }
+
+       if (FEATURE_ON(E2P_FEATURE_COMPAT, EXT4_FEATURE_COMPAT_ORPHAN_FILE)) {
+               if (!ext2fs_has_feature_journal(sb)) {
+                       fputs(_("orphan_file feature can be set only for "
+                               "filesystems with journal.\n"), stderr);
+                       return 1;
+               }
+               /*
+                * If adding an orphan file, let the create orphan file
+                * code below handle setting the flag and creating it.
+                * We supply a default size if necessary.
+                */
+               orphan_file_blocks = ext2fs_default_orphan_file_blocks(fs);
+               ext2fs_set_feature_orphan_file(sb);
+       }
+
        if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
                EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
                if (ext2fs_has_feature_meta_bg(sb)) {
@@ -1238,9 +1358,10 @@ mmp_error:
 
        if (FEATURE_OFF(E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX) &&
            ext2fs_has_feature_metadata_csum(sb)) {
-               check_fsck_needed(fs,
+               if (check_fsck_needed(fs,
                        _("Disabling directory index on filesystem with "
-                         "checksums could take some time."));
+                         "checksums could take some time.")))
+                       return 1;
                if (mount_flags & EXT2_MF_MOUNTED) {
                        fputs(_("Cannot disable dir_index on a mounted "
                                "filesystem!\n"), stderr);
@@ -1467,18 +1588,39 @@ mmp_error:
        }
 
        if (FEATURE_ON(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_ENCRYPT)) {
-               if (ext2fs_has_feature_casefold(sb)) {
-                       fputs(_("Cannot enable encrypt feature on filesystems "
-                               "with the encoding feature enabled.\n"),
-                             stderr);
-                       return 1;
-               }
                fs->super->s_encrypt_algos[0] =
                        EXT4_ENCRYPTION_MODE_AES_256_XTS;
                fs->super->s_encrypt_algos[1] =
                        EXT4_ENCRYPTION_MODE_AES_256_CTS;
        }
 
+       if (FEATURE_ON(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_CASEFOLD)) {
+               if (mount_flags & EXT2_MF_MOUNTED) {
+                       fputs(_("The casefold feature may only be enabled when "
+                               "the filesystem is unmounted.\n"), stderr);
+                       return 1;
+               }
+               fs->super->s_encoding = EXT4_ENC_UTF8_12_1;
+               fs->super->s_encoding_flags = e2p_get_encoding_flags(EXT4_ENC_UTF8_12_1);
+               enabling_casefold = 1;
+       }
+
+       if (FEATURE_OFF(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_CASEFOLD)) {
+               if (mount_flags & EXT2_MF_MOUNTED) {
+                       fputs(_("The casefold feature may only be disabled when "
+                               "the filesystem is unmounted.\n"), stderr);
+                       return 1;
+               }
+               if (has_casefold_inode(fs)) {
+                       fputs(_("The casefold feature can't be cleared when "
+                                       "there are inodes with +F flag.\n"), stderr);
+                       return 1;
+               }
+               fs->super->s_encoding = 0;
+               fs->super->s_encoding_flags = 0;
+               enabling_casefold = 0;
+       }
+
        if (FEATURE_ON(E2P_FEATURE_INCOMPAT,
                EXT4_FEATURE_INCOMPAT_CSUM_SEED)) {
                if (!ext2fs_has_feature_metadata_csum(sb)) {
@@ -1543,7 +1685,7 @@ mmp_error:
  */
 static int add_journal(ext2_filsys fs)
 {
-       unsigned long journal_blocks;
+       struct ext2fs_journal_params    jparams;
        errcode_t       retval;
        ext2_filsys     jfs;
        io_manager      io_ptr;
@@ -1589,13 +1731,13 @@ static int add_journal(ext2_filsys fs)
        } else if (journal_size) {
                fputs(_("Creating journal inode: "), stdout);
                fflush(stdout);
-               journal_blocks = figure_journal_size(journal_size, fs);
+               figure_journal_size(&jparams, journal_size, journal_fc_size, fs);
 
                if (journal_location_string)
                        journal_location =
                                parse_num_blocks2(journal_location_string,
                                                  fs->super->s_log_block_size);
-               retval = ext2fs_add_journal_inode2(fs, journal_blocks,
+               retval = ext2fs_add_journal_inode3(fs, &jparams,
                                                   journal_location,
                                                   journal_flags);
                if (retval) {
@@ -1603,8 +1745,12 @@ static int add_journal(ext2_filsys fs)
                        com_err(program_name, retval, "%s",
                                _("\n\twhile trying to create journal file"));
                        return retval;
-               } else
-                       fputs(_("done\n"), stdout);
+               }
+               fs->super->s_overhead_clusters += EXT2FS_NUM_B2C(fs,
+                       jparams.num_journal_blocks + jparams.num_fc_blocks);
+               ext2fs_mark_super_dirty(fs);
+               fputs(_("done\n"), stdout);
+
                /*
                 * If the filesystem wasn't mounted, we need to force
                 * the block group descriptors out.
@@ -1663,12 +1809,15 @@ static int handle_quota_options(ext2_filsys fs)
                if (quota_enable[qtype] == QOPT_ENABLE &&
                    *quota_sb_inump(fs->super, qtype) == 0) {
                        if ((qf_ino = quota_file_exists(fs, qtype)) > 0) {
-                               retval = quota_update_limits(qctx, qf_ino,
-                                                            qtype);
+                               retval = quota_read_all_dquots(qctx, qf_ino,
+                                                              qtype,
+                                                              QREAD_LIMITS);
                                if (retval) {
                                        com_err(program_name, retval,
                                                _("while updating quota limits (%d)"),
                                                qtype);
+                               quota_errout:
+                                       quota_release_context(&qctx);
                                        return 1;
                                }
                        }
@@ -1677,7 +1826,7 @@ static int handle_quota_options(ext2_filsys fs)
                                com_err(program_name, retval,
                                        _("while writing quota file (%d)"),
                                        qtype);
-                               return 1;
+                               goto quota_errout;
                        }
                        /* Enable Quota feature if one of quota enabled */
                        if (!ext2fs_has_feature_quota(fs->super)) {
@@ -1695,7 +1844,7 @@ static int handle_quota_options(ext2_filsys fs)
                                com_err(program_name, retval,
                                        _("while removing quota file (%d)"),
                                        qtype);
-                               return 1;
+                               goto quota_errout;
                        }
                        if (qtype == PRJQUOTA) {
                                ext2fs_clear_feature_project(fs->super);
@@ -1757,7 +1906,7 @@ static void parse_e2label_options(int argc, char ** argv)
        io_options = strchr(argv[1], '?');
        if (io_options)
                *io_options++ = 0;
-       device_name = blkid_get_devname(NULL, argv[1], NULL);
+       device_name = get_devname(NULL, argv[1], NULL);
        if (!device_name) {
                com_err("e2label", 0, _("Unable to resolve '%s'"),
                        argv[1]);
@@ -1816,8 +1965,15 @@ static void parse_tune2fs_options(int argc, char **argv)
        while ((c = getopt(argc, argv, optstring)) != EOF)
                switch (c) {
                case 'c':
+                       open_flag = EXT2_FLAG_RW;
+                       c_flag = 1;
+                       if (strcmp(optarg, "random") == 0) {
+                               max_mount_count = 65536;
+                               break;
+                       }
                        max_mount_count = strtol(optarg, &tmp, 0);
-                       if (*tmp || max_mount_count > 16000) {
+                       if (*tmp || max_mount_count > 16000 ||
+                           max_mount_count < -16000) {
                                com_err(program_name, 0,
                                        _("bad mounts count - %s"),
                                        optarg);
@@ -1825,8 +1981,6 @@ static void parse_tune2fs_options(int argc, char **argv)
                        }
                        if (max_mount_count == 0)
                                max_mount_count = -1;
-                       c_flag = 1;
-                       open_flag = EXT2_FLAG_RW;
                        break;
                case 'C':
                        mount_count = strtoul(optarg, &tmp, 0);
@@ -2015,7 +2169,7 @@ static void parse_tune2fs_options(int argc, char **argv)
                                open_flag = EXT2_FLAG_RW;
                                break;
                case 'U':
-                       new_UUID = optarg;
+                       requested_uuid = optarg;
                        U_flag = 1;
                        open_flag = EXT2_FLAG_RW |
                                EXT2_FLAG_JOURNAL_DEV_OK;
@@ -2052,7 +2206,7 @@ static void parse_tune2fs_options(int argc, char **argv)
        io_options = strchr(argv[optind], '?');
        if (io_options)
                *io_options++ = 0;
-       device_name = blkid_get_devname(NULL, argv[optind], NULL);
+       device_name = get_devname(NULL, argv[optind], NULL);
        if (!device_name) {
                com_err(program_name, 0, _("Unable to resolve '%s'"),
                        argv[optind]);
@@ -2083,9 +2237,12 @@ void do_findfs(int argc, char **argv)
 
 static int parse_extended_opts(ext2_filsys fs, const char *opts)
 {
+       struct ext2_super_block *sb = fs->super;
        char    *buf, *token, *next, *p, *arg;
        int     len, hash_alg;
        int     r_usage = 0;
+       int encoding = 0;
+       char    *encoding_flags = NULL;
 
        len = strlen(opts);
        buf = malloc(len+1);
@@ -2138,18 +2295,18 @@ static int parse_extended_opts(ext2_filsys fs, const char *opts)
                                  "Setting multiple mount protection update "
                                  "interval to %lu seconds\n", intv),
                               intv);
-                       fs->super->s_mmp_update_interval = intv;
+                       sb->s_mmp_update_interval = intv;
                        ext2fs_mark_super_dirty(fs);
                } else if (!strcmp(token, "force_fsck")) {
-                       fs->super->s_state |= EXT2_ERROR_FS;
+                       sb->s_state |= EXT2_ERROR_FS;
                        printf(_("Setting filesystem error flag to force fsck.\n"));
                        ext2fs_mark_super_dirty(fs);
                } else if (!strcmp(token, "test_fs")) {
-                       fs->super->s_flags |= EXT2_FLAGS_TEST_FILESYS;
+                       sb->s_flags |= EXT2_FLAGS_TEST_FILESYS;
                        printf("Setting test filesystem flag\n");
                        ext2fs_mark_super_dirty(fs);
                } else if (!strcmp(token, "^test_fs")) {
-                       fs->super->s_flags &= ~EXT2_FLAGS_TEST_FILESYS;
+                       sb->s_flags &= ~EXT2_FLAGS_TEST_FILESYS;
                        printf("Clearing test filesystem flag\n");
                        ext2fs_mark_super_dirty(fs);
                } else if (strcmp(token, "stride") == 0) {
@@ -2195,7 +2352,7 @@ static int parse_extended_opts(ext2_filsys fs, const char *opts)
                                r_usage++;
                                continue;
                        }
-                       fs->super->s_def_hash_version = hash_alg;
+                       sb->s_def_hash_version = hash_alg;
                        printf(_("Setting default hash algorithm "
                                 "to %s (%d)\n"),
                               arg, hash_alg);
@@ -2211,9 +2368,78 @@ static int parse_extended_opts(ext2_filsys fs, const char *opts)
                                continue;
                        }
                        ext_mount_opts = strdup(arg);
+               } else if (!strcmp(token, "encoding")) {
+                       if (!arg) {
+                               r_usage++;
+                               continue;
+                       }
+                       if (mount_flags & EXT2_MF_MOUNTED) {
+                               fputs(_("The casefold feature may only be enabled when "
+                                       "the filesystem is unmounted.\n"), stderr);
+                               r_usage++;
+                               continue;
+                       }
+                       if (ext2fs_has_feature_casefold(sb) && !enabling_casefold) {
+                               fprintf(stderr, _("Cannot alter existing encoding\n"));
+                               r_usage++;
+                               continue;
+                       }
+                       encoding = e2p_str2encoding(arg);
+                       if (encoding < 0) {
+                               fprintf(stderr, _("Invalid encoding: %s\n"), arg);
+                               r_usage++;
+                               continue;
+                       }
+                       enabling_casefold = 1;
+                       sb->s_encoding = encoding;
+                       printf(_("Setting encoding to '%s'\n"), arg);
+                       sb->s_encoding_flags =
+                               e2p_get_encoding_flags(sb->s_encoding);
+               } else if (!strcmp(token, "encoding_flags")) {
+                       if (!arg) {
+                               r_usage++;
+                               continue;
+                       }
+                       encoding_flags = arg;
+               } else if (!strcmp(token, "orphan_file_size")) {
+                       if (!arg) {
+                               r_usage++;
+                               continue;
+                       }
+                       orphan_file_blocks = parse_num_blocks2(arg,
+                                                fs->super->s_log_block_size);
+
+                       if (orphan_file_blocks < 1) {
+                               fprintf(stderr,
+                                       _("Invalid size of orphan file %s\n"),
+                                       arg);
+                               r_usage++;
+                               continue;
+                       }
                } else
                        r_usage++;
        }
+
+       if (encoding > 0 && !r_usage) {
+               sb->s_encoding_flags =
+                       e2p_get_encoding_flags(sb->s_encoding);
+
+               if (encoding_flags &&
+                   e2p_str2encoding_flags(sb->s_encoding, encoding_flags,
+                                          &sb->s_encoding_flags)) {
+                       fprintf(stderr, _("error: Invalid encoding flag: %s\n"),
+                                       encoding_flags);
+                       r_usage++;
+               } else if (encoding_flags)
+                       printf(_("Setting encoding_flags to '%s'\n"),
+                                encoding_flags);
+               ext2fs_set_feature_casefold(sb);
+               ext2fs_mark_super_dirty(fs);
+       } else if (encoding_flags && !r_usage) {
+               fprintf(stderr, _("error: An encoding must be explicitly "
+                                 "specified when passing encoding-flags\n"));
+               r_usage++;
+       }
        if (r_usage) {
                fprintf(stderr, "%s", _("\nBad options specified.\n\n"
                        "Extended options are separated by commas, "
@@ -2228,7 +2454,9 @@ static int parse_extended_opts(ext2_filsys fs, const char *opts)
                        "\tstripe_width=<RAID stride*data disks in blocks>\n"
                        "\tforce_fsck\n"
                        "\ttest_fs\n"
-                       "\t^test_fs\n"));
+                       "\t^test_fs\n"
+                       "\tencoding=<encoding>\n"
+                       "\tencoding_flags=<flags>\n"));
                free(buf);
                return 1;
        }
@@ -2848,6 +3076,79 @@ fs_update_journal_user(struct ext2_super_block *sb, __u8 old_uuid[UUID_SIZE])
        return 0;
 }
 
+/*
+ * Use FS_IOC_SETFSLABEL or FS_IOC_GETFSLABEL to set/get file system label
+ * Return:     0 on success
+ *             1 on error
+ *             -1 when the old method should be used
+ */
+int handle_fslabel(int setlabel) {
+       errcode_t ret;
+       int mnt_flags, fd;
+       char label[FSLABEL_MAX];
+       int maxlen = FSLABEL_MAX - 1;
+       char mntpt[PATH_MAX + 1];
+
+#ifdef __linux__
+       ret = ext2fs_check_mount_point(device_name, &mnt_flags,
+                                         mntpt, sizeof(mntpt));
+       if (ret) {
+               com_err(device_name, ret, _("while checking mount status"));
+               return 1;
+       }
+       if (!(mnt_flags & EXT2_MF_MOUNTED) ||
+           (setlabel && (mnt_flags & EXT2_MF_READONLY)))
+               return -1;
+
+       if (!mntpt[0]) {
+               fprintf(stderr,_("Unknown mount point for %s\n"), device_name);
+               return 1;
+       }
+
+       fd = open(mntpt, O_RDONLY);
+       if (fd < 0) {
+               com_err(mntpt, errno, _("while opening mount point"));
+               return 1;
+       }
+
+       /* Get fs label */
+       if (!setlabel) {
+               if (ioctl(fd, FS_IOC_GETFSLABEL, &label)) {
+                       close(fd);
+                       if (errno == ENOTTY)
+                               return -1;
+                       com_err(mntpt, errno, _("while trying to get fs label"));
+                       return 1;
+               }
+               close(fd);
+               printf("%.*s\n", EXT2_LEN_STR(label));
+               return 0;
+       }
+
+       /* If it's extN file system, truncate the label to appropriate size */
+       if (mnt_flags & EXT2_MF_EXTFS)
+               maxlen = EXT2_LABEL_LEN;
+       if (strlen(new_label) > maxlen) {
+               fputs(_("Warning: label too long, truncating.\n"),
+                     stderr);
+               new_label[maxlen] = '\0';
+       }
+
+       /* Set fs label */
+       if (ioctl(fd, FS_IOC_SETFSLABEL, new_label)) {
+               close(fd);
+               if (errno == ENOTTY)
+                       return -1;
+               com_err(mntpt, errno, _("while trying to set fs label"));
+               return 1;
+       }
+       close(fd);
+       return 0;
+#else
+       return -1;
+#endif
+}
+
 #ifndef BUILD_AS_LIB
 int main(int argc, char **argv)
 #else
@@ -2860,6 +3161,9 @@ int tune2fs_main(int argc, char **argv)
        io_manager io_ptr, io_ptr_orig = NULL;
        int rc = 0;
        char default_undo_file[1] = { 0 };
+       char mntpt[PATH_MAX + 1] = { 0 };
+       int fd = -1;
+       struct fsuuid *fsuuid = NULL;
 
 #ifdef ENABLE_NLS
        setlocale(LC_MESSAGES, "");
@@ -2870,6 +3174,8 @@ int tune2fs_main(int argc, char **argv)
 #endif
        if (argc && *argv)
                program_name = *argv;
+       else
+               usage();
        add_error_table(&et_ext2_error_table);
 
 #ifdef CONFIG_BUILD_FINDFS
@@ -2889,6 +3195,21 @@ int tune2fs_main(int argc, char **argv)
 #endif
                io_ptr = unix_io_manager;
 
+       /*
+        * Try the get/set fs label using ioctls before we even attempt
+        * to open the file system.
+        */
+       if (L_flag || print_label) {
+               rc = handle_fslabel(L_flag);
+               if (rc != -1) {
+#ifndef BUILD_AS_LIB
+                       exit(rc);
+#endif
+                       return rc;
+               }
+               rc = 0;
+       }
+
 retry_open:
        if ((open_flag & EXT2_FLAG_RW) == 0 || f_flag)
                open_flag |= EXT2_FLAG_SKIP_MMP;
@@ -2992,9 +3313,10 @@ retry_open:
                goto closefs;
        }
 
-       retval = ext2fs_check_if_mounted(device_name, &mount_flags);
+       retval = ext2fs_check_mount_point(device_name, &mount_flags,
+                                       mntpt, sizeof(mntpt));
        if (retval) {
-               com_err("ext2fs_check_if_mount", retval,
+               com_err("ext2fs_check_mount_point", retval,
                        _("while determining whether %s is mounted."),
                        device_name);
                rc = 1;
@@ -3035,6 +3357,9 @@ _("Warning: The journal is dirty. You may wish to replay the journal like:\n\n"
        fs->flags |= EXT2_FLAG_SUPER_ONLY;
 
        if (c_flag) {
+               if (max_mount_count == 65536)
+                       max_mount_count = EXT2_DFL_MAX_MNT_COUNT +
+                               (random() % EXT2_DFL_MAX_MNT_COUNT);
                sb->s_max_mnt_count = max_mount_count;
                ext2fs_mark_super_dirty(fs);
                printf(_("Setting maximal mount count to %d\n"),
@@ -3073,20 +3398,21 @@ _("Warning: The journal is dirty. You may wish to replay the journal like:\n\n"
                                          ext2fs_blocks_count(sb) / 100.0);
                ext2fs_mark_super_dirty(fs);
                printf (_("Setting reserved blocks percentage to %g%% (%llu blocks)\n"),
-                       reserved_ratio, ext2fs_r_blocks_count(sb));
+                       reserved_ratio,
+                       (unsigned long long) ext2fs_r_blocks_count(sb));
        }
        if (r_flag) {
                if (reserved_blocks > ext2fs_blocks_count(sb)/2) {
                        com_err(program_name, 0,
                                _("reserved blocks count is too big (%llu)"),
-                               reserved_blocks);
+                               (unsigned long long) reserved_blocks);
                        rc = 1;
                        goto closefs;
                }
                ext2fs_r_blocks_count_set(sb, reserved_blocks);
                ext2fs_mark_super_dirty(fs);
                printf(_("Setting reserved blocks count to %llu\n"),
-                      reserved_blocks);
+                      (unsigned long long) reserved_blocks);
        }
        if (s_flag == 1) {
                if (ext2fs_has_feature_sparse_super(sb)) {
@@ -3169,6 +3495,24 @@ _("Warning: The journal is dirty. You may wish to replay the journal like:\n\n"
                if (rc)
                        goto closefs;
        }
+       if (orphan_file_blocks) {
+               errcode_t err;
+
+               err = ext2fs_read_bitmaps(fs);
+               if (err) {
+                       com_err(program_name, err, "%s",
+                               _("while loading bitmaps"));
+                       rc = 1;
+                       goto closefs;
+               }
+               err = ext2fs_create_orphan_file(fs, orphan_file_blocks);
+               if (err) {
+                       com_err(program_name, err, "%s",
+                               _("while creating orphan file"));
+                       rc = 1;
+                       goto closefs;
+               }
+       }
 
        if (Q_flag) {
                if (mount_flags & EXT2_MF_MOUNTED) {
@@ -3187,6 +3531,8 @@ _("Warning: The journal is dirty. You may wish to replay the journal like:\n\n"
                dgrp_t i;
                char buf[SUPERBLOCK_SIZE] __attribute__ ((aligned(8)));
                __u8 old_uuid[UUID_SIZE];
+               uuid_t new_uuid;
+               errcode_t ret = -1;
 
                if (ext2fs_has_feature_stable_inodes(fs->super)) {
                        fputs(_("Cannot change the UUID of this filesystem "
@@ -3240,25 +3586,62 @@ _("Warning: The journal is dirty. You may wish to replay the journal like:\n\n"
                                set_csum = 1;
                }
 
-               memcpy(old_uuid, sb->s_uuid, UUID_SIZE);
-               if ((strcasecmp(new_UUID, "null") == 0) ||
-                   (strcasecmp(new_UUID, "clear") == 0)) {
-                       uuid_clear(sb->s_uuid);
-               } else if (strcasecmp(new_UUID, "time") == 0) {
-                       uuid_generate_time(sb->s_uuid);
-               } else if (strcasecmp(new_UUID, "random") == 0) {
-                       uuid_generate(sb->s_uuid);
-               } else if (uuid_parse(new_UUID, sb->s_uuid)) {
+#ifdef __linux__
+               if ((mount_flags & EXT2_MF_MOUNTED) &&
+                   !(mount_flags & EXT2_MF_READONLY) && mntpt[0]) {
+                       fd = open(mntpt, O_RDONLY);
+                       if (fd >= 0)
+                               fsuuid = malloc(sizeof(*fsuuid) + UUID_SIZE);
+                       if (fsuuid) {
+                               fsuuid->fsu_len = UUID_SIZE;
+                               fsuuid->fsu_flags = 0;
+                               ret = ioctl(fd, EXT4_IOC_GETFSUUID, fsuuid);
+                               if (ret || fsuuid->fsu_len != UUID_SIZE) {
+                                       free(fsuuid);
+                                       fsuuid = NULL;
+                               }
+                       }
+               }
+#endif
+
+               memcpy(old_uuid, fsuuid ? fsuuid->fsu_uuid : sb->s_uuid,
+                      UUID_SIZE);
+               if ((strcasecmp(requested_uuid, "null") == 0) ||
+                   (strcasecmp(requested_uuid, "clear") == 0)) {
+                       uuid_clear(new_uuid);
+               } else if (strcasecmp(requested_uuid, "time") == 0) {
+                       uuid_generate_time(new_uuid);
+               } else if (strcasecmp(requested_uuid, "random") == 0) {
+                       uuid_generate(new_uuid);
+               } else if (uuid_parse(requested_uuid, new_uuid)) {
                        com_err(program_name, 0, "%s",
                                _("Invalid UUID format\n"));
                        rc = 1;
                        goto closefs;
                }
-               ext2fs_init_csum_seed(fs);
-               if (set_csum) {
-                       for (i = 0; i < fs->group_desc_count; i++)
-                               ext2fs_group_desc_csum_set(fs, i);
-                       fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+
+               ret = -1;
+#ifdef __linux__
+               if (fsuuid) {
+                       fsuuid->fsu_len - UUID_SIZE;
+                       fsuuid->fsu_flags = 0;
+                       memcpy(&fsuuid->fsu_uuid, new_uuid, UUID_SIZE);
+                       ret = ioctl(fd, EXT4_IOC_SETFSUUID, fsuuid);
+               }
+#endif
+               /*
+                * If we can't set the UUID via the ioctl, fall
+                * back to directly modifying the superblock
+                .*/
+               if (ret) {
+                       memcpy(sb->s_uuid, new_uuid, UUID_SIZE);
+                       ext2fs_init_csum_seed(fs);
+                       if (set_csum) {
+                               for (i = 0; i < fs->group_desc_count; i++)
+                                       ext2fs_group_desc_csum_set(fs, i);
+                               fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+                       }
+                       ext2fs_mark_super_dirty(fs);
                }
 
                /* If this is a journal dev, we need to copy UUID into jsb */
@@ -3282,8 +3665,6 @@ _("Warning: The journal is dirty. You may wish to replay the journal like:\n\n"
                        if ((rc = fs_update_journal_user(sb, old_uuid)))
                                goto closefs;
                }
-
-               ext2fs_mark_super_dirty(fs);
        }
 
        if (I_flag) {
@@ -3352,6 +3733,10 @@ _("Warning: The journal is dirty. You may wish to replay the journal like:\n\n"
        remove_error_table(&et_ext2_error_table);
 
 closefs:
+       if (fd >= 0)
+               close(fd);
+       if (fsuuid)
+               free(fsuuid);
        if (rc) {
                ext2fs_mmp_stop(fs);
 #ifndef BUILD_AS_LIB