// SPDX-License-Identifier: GPL-2.0-only /* * In memory quota format relies on quota infrastructure to store dquot * information for us. While conventional quota formats for file systems * with persistent storage can load quota information into dquot from the * storage on-demand and hence quota dquot shrinker can free any dquot * that is not currently being used, it must be avoided here. Otherwise we * can lose valuable information, user provided limits, because there is * no persistent storage to load the information from afterwards. * * One information that in-memory quota format needs to keep track of is * a sorted list of ids for each quota type. This is done by utilizing * an rb tree which root is stored in mem_dqinfo->dqi_priv for each quota * type. * * This format can be used to support quota on file system without persistent * storage such as tmpfs. * * Author: Lukas Czerner <[email protected]> * Carlos Maiolino <[email protected]> * * Copyright (C) 2023 Red Hat, Inc. */ #include <linux/errno.h> #include <linux/fs.h> #include <linux/mount.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/rbtree.h> #include <linux/shmem_fs.h> #include <linux/quotaops.h> #include <linux/quota.h> #ifdef CONFIG_TMPFS_QUOTA /* * The following constants define the amount of time given a user * before the soft limits are treated as hard limits (usually resulting * in an allocation failure). The timer is started when the user crosses * their soft limit, it is reset when they go below their soft limit. */ #define SHMEM_MAX_IQ_TIME … #define SHMEM_MAX_DQ_TIME … struct quota_id { … }; static int shmem_check_quota_file(struct super_block *sb, int type) { … } /* * There is no real quota file. Just allocate rb_root for quota ids and * set limits */ static int shmem_read_file_info(struct super_block *sb, int type) { … } static int shmem_write_file_info(struct super_block *sb, int type) { … } /* * Free all the quota_id entries in the rb tree and rb_root. */ static int shmem_free_file_info(struct super_block *sb, int type) { … } static int shmem_get_next_id(struct super_block *sb, struct kqid *qid) { … } /* * Load dquot with limits from existing entry, or create the new entry if * it does not exist. */ static int shmem_acquire_dquot(struct dquot *dquot) { … } static bool shmem_is_empty_dquot(struct dquot *dquot) { … } /* * Store limits from dquot in the tree unless it's fake. If it is fake * remove the id from the tree since there is no useful information in * there. */ static int shmem_release_dquot(struct dquot *dquot) { … } static int shmem_mark_dquot_dirty(struct dquot *dquot) { … } static int shmem_dquot_write_info(struct super_block *sb, int type) { … } static const struct quota_format_ops shmem_format_ops = …; struct quota_format_type shmem_quota_format = …; const struct dquot_operations shmem_quota_operations = …; #endif /* CONFIG_TMPFS_QUOTA */