XFS_SB_VERSION2_PROJID32BIT    | \
         XFS_SB_VERSION2_FTYPE)
 
+/* Maximum size of the xfs filesystem label, no terminating NULL */
+#define XFSLABEL_MAX                   12
+
 /*
  * Superblock - in core version.  Must match the ondisk version below.
  * Must be padded to 64 bit alignment.
        uint16_t        sb_sectsize;    /* volume sector size, bytes */
        uint16_t        sb_inodesize;   /* inode size, bytes */
        uint16_t        sb_inopblock;   /* inodes per block */
-       char            sb_fname[12];   /* file system name */
+       char            sb_fname[XFSLABEL_MAX]; /* file system name */
        uint8_t         sb_blocklog;    /* log2 of sb_blocksize */
        uint8_t         sb_sectlog;     /* log2 of sb_sectsize */
        uint8_t         sb_inodelog;    /* log2 of sb_inodesize */
        __be16          sb_sectsize;    /* volume sector size, bytes */
        __be16          sb_inodesize;   /* inode size, bytes */
        __be16          sb_inopblock;   /* inodes per block */
-       char            sb_fname[12];   /* file system name */
+       char            sb_fname[XFSLABEL_MAX]; /* file system name */
        __u8            sb_blocklog;    /* log2 of sb_blocksize */
        __u8            sb_sectlog;     /* log2 of sb_sectsize */
        __u8            sb_inodelog;    /* log2 of sb_inodesize */
 
        return saved_error ? saved_error : error;
 }
 
+/*
+ * Same behavior as xfs_sync_sb, except that it is always synchronous and it
+ * also writes the superblock buffer to disk sector 0 immediately.
+ */
+int
+xfs_sync_sb_buf(
+       struct xfs_mount        *mp)
+{
+       struct xfs_trans        *tp;
+       int                     error;
+
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_sb, 0, 0, 0, &tp);
+       if (error)
+               return error;
+
+       xfs_log_sb(tp);
+       xfs_trans_bhold(tp, mp->m_sb_bp);
+       xfs_trans_set_sync(tp);
+       error = xfs_trans_commit(tp);
+       if (error)
+               goto out;
+       /*
+        * write out the sb buffer to get the changes to disk
+        */
+       error = xfs_bwrite(mp->m_sb_bp);
+out:
+       xfs_buf_relse(mp->m_sb_bp);
+       return error;
+}
+
 int
 xfs_fs_geometry(
        struct xfs_sb           *sbp,
 
        return error;
 }
 
+static int
+xfs_ioc_getlabel(
+       struct xfs_mount        *mp,
+       char                    __user *user_label)
+{
+       struct xfs_sb           *sbp = &mp->m_sb;
+       char                    label[XFSLABEL_MAX + 1];
+
+       /* Paranoia */
+       BUILD_BUG_ON(sizeof(sbp->sb_fname) > FSLABEL_MAX);
+
+       spin_lock(&mp->m_sb_lock);
+       strncpy(label, sbp->sb_fname, sizeof(sbp->sb_fname));
+       spin_unlock(&mp->m_sb_lock);
+
+       /* xfs on-disk label is 12 chars, be sure we send a null to user */
+       label[XFSLABEL_MAX] = '\0';
+       if (copy_to_user(user_label, label, sizeof(sbp->sb_fname)))
+               return -EFAULT;
+       return 0;
+}
+
+static int
+xfs_ioc_setlabel(
+       struct file             *filp,
+       struct xfs_mount        *mp,
+       char                    __user *newlabel)
+{
+       struct xfs_sb           *sbp = &mp->m_sb;
+       char                    label[XFSLABEL_MAX + 1];
+       size_t                  len;
+       int                     error;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+       /*
+        * The generic ioctl allows up to FSLABEL_MAX chars, but XFS is much
+        * smaller, at 12 bytes.  We copy one more to be sure we find the
+        * (required) NULL character to test the incoming label length.
+        * NB: The on disk label doesn't need to be null terminated.
+        */
+       if (copy_from_user(label, newlabel, XFSLABEL_MAX + 1))
+               return -EFAULT;
+       len = strnlen(label, XFSLABEL_MAX + 1);
+       if (len > sizeof(sbp->sb_fname))
+               return -EINVAL;
+
+       error = mnt_want_write_file(filp);
+       if (error)
+               return error;
+
+       spin_lock(&mp->m_sb_lock);
+       memset(sbp->sb_fname, 0, sizeof(sbp->sb_fname));
+       strncpy(sbp->sb_fname, label, sizeof(sbp->sb_fname));
+       spin_unlock(&mp->m_sb_lock);
+
+       /*
+        * Now we do several things to satisfy userspace.
+        * In addition to normal logging of the primary superblock, we also
+        * immediately write these changes to sector zero for the primary, then
+        * update all backup supers (as xfs_db does for a label change), then
+        * invalidate the block device page cache.  This is so that any prior
+        * buffered reads from userspace (i.e. from blkid) are invalidated,
+        * and userspace will see the newly-written label.
+        */
+       error = xfs_sync_sb_buf(mp);
+       if (error)
+               goto out;
+       /*
+        * growfs also updates backup supers so lock against that.
+        */
+       mutex_lock(&mp->m_growlock);
+       error = xfs_update_secondary_sbs(mp);
+       mutex_unlock(&mp->m_growlock);
+
+       invalidate_bdev(mp->m_ddev_targp->bt_bdev);
+
+out:
+       mnt_drop_write_file(filp);
+       return error;
+}
+
 /*
  * Note: some of the ioctl's return positive numbers as a
  * byte count indicating success, such as readlink_by_handle.
        switch (cmd) {
        case FITRIM:
                return xfs_ioc_trim(mp, arg);
+       case FS_IOC_GETFSLABEL:
+               return xfs_ioc_getlabel(mp, arg);
+       case FS_IOC_SETFSLABEL:
+               return xfs_ioc_setlabel(filp, mp, arg);
        case XFS_IOC_ALLOCSP:
        case XFS_IOC_FREESP:
        case XFS_IOC_RESVSP: