#include <uapi/linux/memfd.h>
 #include <linux/rmap.h>
 #include <linux/uuid.h>
+#include <linux/quotaops.h>
 
 #include <linux/uaccess.h>
 
        int huge;
        int seen;
        bool noswap;
+       unsigned short quota_types;
 #define SHMEM_SEEN_BLOCKS 1
 #define SHMEM_SEEN_INODES 2
 #define SHMEM_SEEN_HUGE 4
 #define SHMEM_SEEN_INUMS 8
 #define SHMEM_SEEN_NOSWAP 16
+#define SHMEM_SEEN_QUOTA 32
 };
 
 #ifdef CONFIG_TMPFS
                if (percpu_counter_compare(&sbinfo->used_blocks,
                                           sbinfo->max_blocks - pages) > 0)
                        goto unacct;
+
+               err = dquot_alloc_block_nodirty(inode, pages);
+               if (err)
+                       goto unacct;
+
                percpu_counter_add(&sbinfo->used_blocks, pages);
+       } else {
+               err = dquot_alloc_block_nodirty(inode, pages);
+               if (err)
+                       goto unacct;
        }
 
        return 0;
        struct shmem_inode_info *info = SHMEM_I(inode);
        struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
 
+       dquot_free_block_nodirty(inode, pages);
+
        if (sbinfo->max_blocks)
                percpu_counter_sub(&sbinfo->used_blocks, pages);
        shmem_unacct_blocks(info->flags, pages);
 static LIST_HEAD(shmem_swaplist);
 static DEFINE_MUTEX(shmem_swaplist_mutex);
 
+#ifdef CONFIG_TMPFS_QUOTA
+
+static int shmem_enable_quotas(struct super_block *sb,
+                              unsigned short quota_types)
+{
+       int type, err = 0;
+
+       sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE | DQUOT_NOLIST_DIRTY;
+       for (type = 0; type < SHMEM_MAXQUOTAS; type++) {
+               if (!(quota_types & (1 << type)))
+                       continue;
+               err = dquot_load_quota_sb(sb, type, QFMT_SHMEM,
+                                         DQUOT_USAGE_ENABLED |
+                                         DQUOT_LIMITS_ENABLED);
+               if (err)
+                       goto out_err;
+       }
+       return 0;
+
+out_err:
+       pr_warn("tmpfs: failed to enable quota tracking (type=%d, err=%d)\n",
+               type, err);
+       for (type--; type >= 0; type--)
+               dquot_quota_off(sb, type);
+       return err;
+}
+
+static void shmem_disable_quotas(struct super_block *sb)
+{
+       int type;
+
+       for (type = 0; type < SHMEM_MAXQUOTAS; type++)
+               dquot_quota_off(sb, type);
+}
+
+static struct dquot **shmem_get_dquots(struct inode *inode)
+{
+       return SHMEM_I(inode)->i_dquot;
+}
+#endif /* CONFIG_TMPFS_QUOTA */
+
 /*
  * shmem_reserve_inode() performs bookkeeping to reserve a shmem inode, and
  * produces a novel ino for the newly allocated inode.
        freed = info->alloced - info->swapped - inode->i_mapping->nrpages;
        if (freed > 0) {
                info->alloced -= freed;
-               inode->i_blocks -= freed * BLOCKS_PER_PAGE;
                shmem_inode_unacct_blocks(inode, freed);
        }
 }
 
        spin_lock_irqsave(&info->lock, flags);
        info->alloced += pages;
-       inode->i_blocks += pages * BLOCKS_PER_PAGE;
        shmem_recalc_inode(inode);
        spin_unlock_irqrestore(&info->lock, flags);
 
 
        spin_lock_irqsave(&info->lock, flags);
        info->alloced -= pages;
-       inode->i_blocks -= pages * BLOCKS_PER_PAGE;
        shmem_recalc_inode(inode);
        spin_unlock_irqrestore(&info->lock, flags);
 
                }
        }
 
+       if (is_quota_modification(idmap, inode, attr)) {
+               error = dquot_initialize(inode);
+               if (error)
+                       return error;
+       }
+
+       /* Transfer quota accounting */
+       if (i_uid_needs_update(idmap, attr, inode) ||
+           i_gid_needs_update(idmap, attr, inode)) {
+               error = dquot_transfer(idmap, inode, attr);
+
+               if (error)
+                       return error;
+       }
+
        setattr_copy(idmap, inode, attr);
        if (attr->ia_valid & ATTR_MODE)
                error = posix_acl_chmod(idmap, dentry, inode->i_mode);
        WARN_ON(inode->i_blocks);
        shmem_free_inode(inode->i_sb);
        clear_inode(inode);
+#ifdef CONFIG_TMPFS_QUOTA
+       dquot_free_inode(inode);
+       dquot_drop(inode);
+#endif
 }
 
 static int shmem_find_swap_entries(struct address_space *mapping,
 
        spin_lock_irq(&info->lock);
        info->alloced += folio_nr_pages(folio);
-       inode->i_blocks += (blkcnt_t)BLOCKS_PER_PAGE << folio_order(folio);
        shmem_recalc_inode(inode);
        spin_unlock_irq(&info->lock);
        alloced = true;
 #define shmem_initxattrs NULL
 #endif
 
-static struct inode *shmem_get_inode(struct mnt_idmap *idmap, struct super_block *sb,
-                                    struct inode *dir, umode_t mode, dev_t dev,
-                                    unsigned long flags)
+static struct inode *__shmem_get_inode(struct mnt_idmap *idmap,
+                                            struct super_block *sb,
+                                            struct inode *dir, umode_t mode,
+                                            dev_t dev, unsigned long flags)
 {
        struct inode *inode;
        struct shmem_inode_info *info;
        return inode;
 }
 
+#ifdef CONFIG_TMPFS_QUOTA
+static struct inode *shmem_get_inode(struct mnt_idmap *idmap,
+                                    struct super_block *sb, struct inode *dir,
+                                    umode_t mode, dev_t dev, unsigned long flags)
+{
+       int err;
+       struct inode *inode;
+
+       inode = __shmem_get_inode(idmap, sb, dir, mode, dev, flags);
+       if (IS_ERR(inode))
+               return inode;
+
+       err = dquot_initialize(inode);
+       if (err)
+               goto errout;
+
+       err = dquot_alloc_inode(inode);
+       if (err) {
+               dquot_drop(inode);
+               goto errout;
+       }
+       return inode;
+
+errout:
+       inode->i_flags |= S_NOQUOTA;
+       iput(inode);
+       return ERR_PTR(err);
+}
+#else
+static inline struct inode *shmem_get_inode(struct mnt_idmap *idmap,
+                                    struct super_block *sb, struct inode *dir,
+                                    umode_t mode, dev_t dev, unsigned long flags)
+{
+       return __shmem_get_inode(idmap, sb, dir, mode, dev, flags);
+}
+#endif /* CONFIG_TMPFS_QUOTA */
+
 #ifdef CONFIG_USERFAULTFD
 int shmem_mfill_atomic_pte(pmd_t *dst_pmd,
                           struct vm_area_struct *dst_vma,
 
        spin_lock_irq(&info->lock);
        info->alloced++;
-       inode->i_blocks += BLOCKS_PER_PAGE;
        shmem_recalc_inode(inode);
        spin_unlock_irq(&info->lock);
 
 
 static const struct inode_operations shmem_short_symlink_operations = {
        .getattr        = shmem_getattr,
+       .setattr        = shmem_setattr,
        .get_link       = simple_get_link,
 #ifdef CONFIG_TMPFS_XATTR
        .listxattr      = shmem_listxattr,
 
 static const struct inode_operations shmem_symlink_inode_operations = {
        .getattr        = shmem_getattr,
+       .setattr        = shmem_setattr,
        .get_link       = shmem_get_link,
 #ifdef CONFIG_TMPFS_XATTR
        .listxattr      = shmem_listxattr,
        Opt_inode32,
        Opt_inode64,
        Opt_noswap,
+       Opt_quota,
+       Opt_usrquota,
+       Opt_grpquota,
 };
 
 static const struct constant_table shmem_param_enums_huge[] = {
        fsparam_flag  ("inode32",       Opt_inode32),
        fsparam_flag  ("inode64",       Opt_inode64),
        fsparam_flag  ("noswap",        Opt_noswap),
+#ifdef CONFIG_TMPFS_QUOTA
+       fsparam_flag  ("quota",         Opt_quota),
+       fsparam_flag  ("usrquota",      Opt_usrquota),
+       fsparam_flag  ("grpquota",      Opt_grpquota),
+#endif
        {}
 };
 
                ctx->noswap = true;
                ctx->seen |= SHMEM_SEEN_NOSWAP;
                break;
+       case Opt_quota:
+               if (fc->user_ns != &init_user_ns)
+                       return invalfc(fc, "Quotas in unprivileged tmpfs mounts are unsupported");
+               ctx->seen |= SHMEM_SEEN_QUOTA;
+               ctx->quota_types |= (QTYPE_MASK_USR | QTYPE_MASK_GRP);
+               break;
+       case Opt_usrquota:
+               if (fc->user_ns != &init_user_ns)
+                       return invalfc(fc, "Quotas in unprivileged tmpfs mounts are unsupported");
+               ctx->seen |= SHMEM_SEEN_QUOTA;
+               ctx->quota_types |= QTYPE_MASK_USR;
+               break;
+       case Opt_grpquota:
+               if (fc->user_ns != &init_user_ns)
+                       return invalfc(fc, "Quotas in unprivileged tmpfs mounts are unsupported");
+               ctx->seen |= SHMEM_SEEN_QUOTA;
+               ctx->quota_types |= QTYPE_MASK_GRP;
+               break;
        }
        return 0;
 
                goto out;
        }
 
+       if (ctx->seen & SHMEM_SEEN_QUOTA &&
+           !sb_any_quota_loaded(fc->root->d_sb)) {
+               err = "Cannot enable quota on remount";
+               goto out;
+       }
+
        if (ctx->seen & SHMEM_SEEN_HUGE)
                sbinfo->huge = ctx->huge;
        if (ctx->seen & SHMEM_SEEN_INUMS)
 {
        struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
 
+#ifdef CONFIG_TMPFS_QUOTA
+       shmem_disable_quotas(sb);
+#endif
        free_percpu(sbinfo->ino_batch);
        percpu_counter_destroy(&sbinfo->used_blocks);
        mpol_put(sbinfo->mpol);
 #endif
        uuid_gen(&sb->s_uuid);
 
+#ifdef CONFIG_TMPFS_QUOTA
+       if (ctx->seen & SHMEM_SEEN_QUOTA) {
+               sb->dq_op = &shmem_quota_operations;
+               sb->s_qcop = &dquot_quotactl_sysfile_ops;
+               sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP;
+
+               if (shmem_enable_quotas(sb, ctx->quota_types))
+                       goto failed;
+       }
+#endif /* CONFIG_TMPFS_QUOTA */
+
        inode = shmem_get_inode(&nop_mnt_idmap, sb, NULL, S_IFDIR | sbinfo->mode, 0,
                                VM_NORESERVE);
        if (IS_ERR(inode)) {
 #ifdef CONFIG_TMPFS
        .statfs         = shmem_statfs,
        .show_options   = shmem_show_options,
+#endif
+#ifdef CONFIG_TMPFS_QUOTA
+       .get_dquots     = shmem_get_dquots,
 #endif
        .evict_inode    = shmem_evict_inode,
        .drop_inode     = generic_delete_inode,
 
        shmem_init_inodecache();
 
+#ifdef CONFIG_TMPFS_QUOTA
+       error = register_quota_format(&shmem_quota_format);
+       if (error < 0) {
+               pr_err("Could not register quota format\n");
+               goto out3;
+       }
+#endif
+
        error = register_filesystem(&shmem_fs_type);
        if (error) {
                pr_err("Could not register tmpfs\n");
 out1:
        unregister_filesystem(&shmem_fs_type);
 out2:
+#ifdef CONFIG_TMPFS_QUOTA
+       unregister_quota_format(&shmem_quota_format);
+out3:
+#endif
        shmem_destroy_inodecache();
        shm_mnt = ERR_PTR(error);
 }