|
@@ -211,8 +211,6 @@ static struct hlist_head *dquot_hash;
|
|
|
|
|
|
struct dqstats dqstats;
|
|
struct dqstats dqstats;
|
|
|
|
|
|
-static void dqput(struct dquot *dquot);
|
|
|
|
-
|
|
|
|
static inline unsigned int
|
|
static inline unsigned int
|
|
hashfn(const struct super_block *sb, unsigned int id, int type)
|
|
hashfn(const struct super_block *sb, unsigned int id, int type)
|
|
{
|
|
{
|
|
@@ -415,6 +413,17 @@ out_dqlock:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+void dquot_destroy(struct dquot *dquot)
|
|
|
|
+{
|
|
|
|
+ kmem_cache_free(dquot_cachep, dquot);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL(dquot_destroy);
|
|
|
|
+
|
|
|
|
+static inline void do_destroy_dquot(struct dquot *dquot)
|
|
|
|
+{
|
|
|
|
+ dquot->dq_sb->dq_op->destroy_dquot(dquot);
|
|
|
|
+}
|
|
|
|
+
|
|
/* Invalidate all dquots on the list. Note that this function is called after
|
|
/* Invalidate all dquots on the list. Note that this function is called after
|
|
* quota is disabled and pointers from inodes removed so there cannot be new
|
|
* quota is disabled and pointers from inodes removed so there cannot be new
|
|
* quota users. There can still be some users of quotas due to inodes being
|
|
* quota users. There can still be some users of quotas due to inodes being
|
|
@@ -463,9 +472,44 @@ restart:
|
|
remove_dquot_hash(dquot);
|
|
remove_dquot_hash(dquot);
|
|
remove_free_dquot(dquot);
|
|
remove_free_dquot(dquot);
|
|
remove_inuse(dquot);
|
|
remove_inuse(dquot);
|
|
- kmem_cache_free(dquot_cachep, dquot);
|
|
|
|
|
|
+ do_destroy_dquot(dquot);
|
|
|
|
+ }
|
|
|
|
+ spin_unlock(&dq_list_lock);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Call callback for every active dquot on given filesystem */
|
|
|
|
+int dquot_scan_active(struct super_block *sb,
|
|
|
|
+ int (*fn)(struct dquot *dquot, unsigned long priv),
|
|
|
|
+ unsigned long priv)
|
|
|
|
+{
|
|
|
|
+ struct dquot *dquot, *old_dquot = NULL;
|
|
|
|
+ int ret = 0;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
|
|
|
|
+ spin_lock(&dq_list_lock);
|
|
|
|
+ list_for_each_entry(dquot, &inuse_list, dq_inuse) {
|
|
|
|
+ if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags))
|
|
|
|
+ continue;
|
|
|
|
+ if (dquot->dq_sb != sb)
|
|
|
|
+ continue;
|
|
|
|
+ /* Now we have active dquot so we can just increase use count */
|
|
|
|
+ atomic_inc(&dquot->dq_count);
|
|
|
|
+ dqstats.lookups++;
|
|
|
|
+ spin_unlock(&dq_list_lock);
|
|
|
|
+ dqput(old_dquot);
|
|
|
|
+ old_dquot = dquot;
|
|
|
|
+ ret = fn(dquot, priv);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ goto out;
|
|
|
|
+ spin_lock(&dq_list_lock);
|
|
|
|
+ /* We are safe to continue now because our dquot could not
|
|
|
|
+ * be moved out of the inuse list while we hold the reference */
|
|
}
|
|
}
|
|
spin_unlock(&dq_list_lock);
|
|
spin_unlock(&dq_list_lock);
|
|
|
|
+out:
|
|
|
|
+ dqput(old_dquot);
|
|
|
|
+ mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
|
|
int vfs_quota_sync(struct super_block *sb, int type)
|
|
int vfs_quota_sync(struct super_block *sb, int type)
|
|
@@ -479,7 +523,7 @@ int vfs_quota_sync(struct super_block *sb, int type)
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
|
if (type != -1 && cnt != type)
|
|
if (type != -1 && cnt != type)
|
|
continue;
|
|
continue;
|
|
- if (!sb_has_quota_enabled(sb, cnt))
|
|
|
|
|
|
+ if (!sb_has_quota_active(sb, cnt))
|
|
continue;
|
|
continue;
|
|
spin_lock(&dq_list_lock);
|
|
spin_lock(&dq_list_lock);
|
|
dirty = &dqopt->info[cnt].dqi_dirty_list;
|
|
dirty = &dqopt->info[cnt].dqi_dirty_list;
|
|
@@ -504,8 +548,8 @@ int vfs_quota_sync(struct super_block *sb, int type)
|
|
}
|
|
}
|
|
|
|
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
|
- if ((cnt == type || type == -1) && sb_has_quota_enabled(sb, cnt)
|
|
|
|
- && info_dirty(&dqopt->info[cnt]))
|
|
|
|
|
|
+ if ((cnt == type || type == -1) && sb_has_quota_active(sb, cnt)
|
|
|
|
+ && info_dirty(&dqopt->info[cnt]))
|
|
sb->dq_op->write_info(sb, cnt);
|
|
sb->dq_op->write_info(sb, cnt);
|
|
spin_lock(&dq_list_lock);
|
|
spin_lock(&dq_list_lock);
|
|
dqstats.syncs++;
|
|
dqstats.syncs++;
|
|
@@ -527,7 +571,7 @@ static void prune_dqcache(int count)
|
|
remove_dquot_hash(dquot);
|
|
remove_dquot_hash(dquot);
|
|
remove_free_dquot(dquot);
|
|
remove_free_dquot(dquot);
|
|
remove_inuse(dquot);
|
|
remove_inuse(dquot);
|
|
- kmem_cache_free(dquot_cachep, dquot);
|
|
|
|
|
|
+ do_destroy_dquot(dquot);
|
|
count--;
|
|
count--;
|
|
head = free_dquots.prev;
|
|
head = free_dquots.prev;
|
|
}
|
|
}
|
|
@@ -558,7 +602,7 @@ static struct shrinker dqcache_shrinker = {
|
|
* NOTE: If you change this function please check whether dqput_blocks() works right...
|
|
* NOTE: If you change this function please check whether dqput_blocks() works right...
|
|
* MUST be called with either dqptr_sem or dqonoff_mutex held
|
|
* MUST be called with either dqptr_sem or dqonoff_mutex held
|
|
*/
|
|
*/
|
|
-static void dqput(struct dquot *dquot)
|
|
|
|
|
|
+void dqput(struct dquot *dquot)
|
|
{
|
|
{
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
@@ -584,7 +628,7 @@ we_slept:
|
|
/* We have more than one user... nothing to do */
|
|
/* We have more than one user... nothing to do */
|
|
atomic_dec(&dquot->dq_count);
|
|
atomic_dec(&dquot->dq_count);
|
|
/* Releasing dquot during quotaoff phase? */
|
|
/* Releasing dquot during quotaoff phase? */
|
|
- if (!sb_has_quota_enabled(dquot->dq_sb, dquot->dq_type) &&
|
|
|
|
|
|
+ if (!sb_has_quota_active(dquot->dq_sb, dquot->dq_type) &&
|
|
atomic_read(&dquot->dq_count) == 1)
|
|
atomic_read(&dquot->dq_count) == 1)
|
|
wake_up(&dquot->dq_wait_unused);
|
|
wake_up(&dquot->dq_wait_unused);
|
|
spin_unlock(&dq_list_lock);
|
|
spin_unlock(&dq_list_lock);
|
|
@@ -625,11 +669,17 @@ we_slept:
|
|
spin_unlock(&dq_list_lock);
|
|
spin_unlock(&dq_list_lock);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+struct dquot *dquot_alloc(struct super_block *sb, int type)
|
|
|
|
+{
|
|
|
|
+ return kmem_cache_zalloc(dquot_cachep, GFP_NOFS);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL(dquot_alloc);
|
|
|
|
+
|
|
static struct dquot *get_empty_dquot(struct super_block *sb, int type)
|
|
static struct dquot *get_empty_dquot(struct super_block *sb, int type)
|
|
{
|
|
{
|
|
struct dquot *dquot;
|
|
struct dquot *dquot;
|
|
|
|
|
|
- dquot = kmem_cache_zalloc(dquot_cachep, GFP_NOFS);
|
|
|
|
|
|
+ dquot = sb->dq_op->alloc_dquot(sb, type);
|
|
if(!dquot)
|
|
if(!dquot)
|
|
return NODQUOT;
|
|
return NODQUOT;
|
|
|
|
|
|
@@ -646,16 +696,34 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type)
|
|
return dquot;
|
|
return dquot;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Check whether dquot is in memory.
|
|
|
|
+ * MUST be called with either dqptr_sem or dqonoff_mutex held
|
|
|
|
+ */
|
|
|
|
+int dquot_is_cached(struct super_block *sb, unsigned int id, int type)
|
|
|
|
+{
|
|
|
|
+ unsigned int hashent = hashfn(sb, id, type);
|
|
|
|
+ int ret = 0;
|
|
|
|
+
|
|
|
|
+ if (!sb_has_quota_active(sb, type))
|
|
|
|
+ return 0;
|
|
|
|
+ spin_lock(&dq_list_lock);
|
|
|
|
+ if (find_dquot(hashent, sb, id, type) != NODQUOT)
|
|
|
|
+ ret = 1;
|
|
|
|
+ spin_unlock(&dq_list_lock);
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Get reference to dquot
|
|
* Get reference to dquot
|
|
* MUST be called with either dqptr_sem or dqonoff_mutex held
|
|
* MUST be called with either dqptr_sem or dqonoff_mutex held
|
|
*/
|
|
*/
|
|
-static struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
|
|
|
|
|
|
+struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
|
|
{
|
|
{
|
|
unsigned int hashent = hashfn(sb, id, type);
|
|
unsigned int hashent = hashfn(sb, id, type);
|
|
struct dquot *dquot, *empty = NODQUOT;
|
|
struct dquot *dquot, *empty = NODQUOT;
|
|
|
|
|
|
- if (!sb_has_quota_enabled(sb, type))
|
|
|
|
|
|
+ if (!sb_has_quota_active(sb, type))
|
|
return NODQUOT;
|
|
return NODQUOT;
|
|
we_slept:
|
|
we_slept:
|
|
spin_lock(&dq_list_lock);
|
|
spin_lock(&dq_list_lock);
|
|
@@ -682,7 +750,7 @@ we_slept:
|
|
dqstats.lookups++;
|
|
dqstats.lookups++;
|
|
spin_unlock(&dq_list_lock);
|
|
spin_unlock(&dq_list_lock);
|
|
if (empty)
|
|
if (empty)
|
|
- kmem_cache_free(dquot_cachep, empty);
|
|
|
|
|
|
+ do_destroy_dquot(empty);
|
|
}
|
|
}
|
|
/* Wait for dq_lock - after this we know that either dquot_release() is already
|
|
/* Wait for dq_lock - after this we know that either dquot_release() is already
|
|
* finished or it will be canceled due to dq_count > 1 test */
|
|
* finished or it will be canceled due to dq_count > 1 test */
|
|
@@ -820,7 +888,7 @@ static void drop_dquot_ref(struct super_block *sb, int type)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-static inline void dquot_incr_inodes(struct dquot *dquot, unsigned long number)
|
|
|
|
|
|
+static inline void dquot_incr_inodes(struct dquot *dquot, qsize_t number)
|
|
{
|
|
{
|
|
dquot->dq_dqb.dqb_curinodes += number;
|
|
dquot->dq_dqb.dqb_curinodes += number;
|
|
}
|
|
}
|
|
@@ -830,9 +898,10 @@ static inline void dquot_incr_space(struct dquot *dquot, qsize_t number)
|
|
dquot->dq_dqb.dqb_curspace += number;
|
|
dquot->dq_dqb.dqb_curspace += number;
|
|
}
|
|
}
|
|
|
|
|
|
-static inline void dquot_decr_inodes(struct dquot *dquot, unsigned long number)
|
|
|
|
|
|
+static inline void dquot_decr_inodes(struct dquot *dquot, qsize_t number)
|
|
{
|
|
{
|
|
- if (dquot->dq_dqb.dqb_curinodes > number)
|
|
|
|
|
|
+ if (sb_dqopt(dquot->dq_sb)->flags & DQUOT_NEGATIVE_USAGE ||
|
|
|
|
+ dquot->dq_dqb.dqb_curinodes >= number)
|
|
dquot->dq_dqb.dqb_curinodes -= number;
|
|
dquot->dq_dqb.dqb_curinodes -= number;
|
|
else
|
|
else
|
|
dquot->dq_dqb.dqb_curinodes = 0;
|
|
dquot->dq_dqb.dqb_curinodes = 0;
|
|
@@ -843,11 +912,12 @@ static inline void dquot_decr_inodes(struct dquot *dquot, unsigned long number)
|
|
|
|
|
|
static inline void dquot_decr_space(struct dquot *dquot, qsize_t number)
|
|
static inline void dquot_decr_space(struct dquot *dquot, qsize_t number)
|
|
{
|
|
{
|
|
- if (dquot->dq_dqb.dqb_curspace > number)
|
|
|
|
|
|
+ if (sb_dqopt(dquot->dq_sb)->flags & DQUOT_NEGATIVE_USAGE ||
|
|
|
|
+ dquot->dq_dqb.dqb_curspace >= number)
|
|
dquot->dq_dqb.dqb_curspace -= number;
|
|
dquot->dq_dqb.dqb_curspace -= number;
|
|
else
|
|
else
|
|
dquot->dq_dqb.dqb_curspace = 0;
|
|
dquot->dq_dqb.dqb_curspace = 0;
|
|
- if (toqb(dquot->dq_dqb.dqb_curspace) <= dquot->dq_dqb.dqb_bsoftlimit)
|
|
|
|
|
|
+ if (dquot->dq_dqb.dqb_curspace <= dquot->dq_dqb.dqb_bsoftlimit)
|
|
dquot->dq_dqb.dqb_btime = (time_t) 0;
|
|
dquot->dq_dqb.dqb_btime = (time_t) 0;
|
|
clear_bit(DQ_BLKS_B, &dquot->dq_flags);
|
|
clear_bit(DQ_BLKS_B, &dquot->dq_flags);
|
|
}
|
|
}
|
|
@@ -1023,10 +1093,11 @@ static inline char ignore_hardlimit(struct dquot *dquot)
|
|
}
|
|
}
|
|
|
|
|
|
/* needs dq_data_lock */
|
|
/* needs dq_data_lock */
|
|
-static int check_idq(struct dquot *dquot, ulong inodes, char *warntype)
|
|
|
|
|
|
+static int check_idq(struct dquot *dquot, qsize_t inodes, char *warntype)
|
|
{
|
|
{
|
|
*warntype = QUOTA_NL_NOWARN;
|
|
*warntype = QUOTA_NL_NOWARN;
|
|
- if (inodes <= 0 || test_bit(DQ_FAKE_B, &dquot->dq_flags))
|
|
|
|
|
|
+ if (!sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_type) ||
|
|
|
|
+ test_bit(DQ_FAKE_B, &dquot->dq_flags))
|
|
return QUOTA_OK;
|
|
return QUOTA_OK;
|
|
|
|
|
|
if (dquot->dq_dqb.dqb_ihardlimit &&
|
|
if (dquot->dq_dqb.dqb_ihardlimit &&
|
|
@@ -1058,11 +1129,12 @@ static int check_idq(struct dquot *dquot, ulong inodes, char *warntype)
|
|
static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *warntype)
|
|
static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *warntype)
|
|
{
|
|
{
|
|
*warntype = QUOTA_NL_NOWARN;
|
|
*warntype = QUOTA_NL_NOWARN;
|
|
- if (space <= 0 || test_bit(DQ_FAKE_B, &dquot->dq_flags))
|
|
|
|
|
|
+ if (!sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_type) ||
|
|
|
|
+ test_bit(DQ_FAKE_B, &dquot->dq_flags))
|
|
return QUOTA_OK;
|
|
return QUOTA_OK;
|
|
|
|
|
|
if (dquot->dq_dqb.dqb_bhardlimit &&
|
|
if (dquot->dq_dqb.dqb_bhardlimit &&
|
|
- toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bhardlimit &&
|
|
|
|
|
|
+ dquot->dq_dqb.dqb_curspace + space > dquot->dq_dqb.dqb_bhardlimit &&
|
|
!ignore_hardlimit(dquot)) {
|
|
!ignore_hardlimit(dquot)) {
|
|
if (!prealloc)
|
|
if (!prealloc)
|
|
*warntype = QUOTA_NL_BHARDWARN;
|
|
*warntype = QUOTA_NL_BHARDWARN;
|
|
@@ -1070,7 +1142,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war
|
|
}
|
|
}
|
|
|
|
|
|
if (dquot->dq_dqb.dqb_bsoftlimit &&
|
|
if (dquot->dq_dqb.dqb_bsoftlimit &&
|
|
- toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bsoftlimit &&
|
|
|
|
|
|
+ dquot->dq_dqb.dqb_curspace + space > dquot->dq_dqb.dqb_bsoftlimit &&
|
|
dquot->dq_dqb.dqb_btime && get_seconds() >= dquot->dq_dqb.dqb_btime &&
|
|
dquot->dq_dqb.dqb_btime && get_seconds() >= dquot->dq_dqb.dqb_btime &&
|
|
!ignore_hardlimit(dquot)) {
|
|
!ignore_hardlimit(dquot)) {
|
|
if (!prealloc)
|
|
if (!prealloc)
|
|
@@ -1079,7 +1151,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war
|
|
}
|
|
}
|
|
|
|
|
|
if (dquot->dq_dqb.dqb_bsoftlimit &&
|
|
if (dquot->dq_dqb.dqb_bsoftlimit &&
|
|
- toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bsoftlimit &&
|
|
|
|
|
|
+ dquot->dq_dqb.dqb_curspace + space > dquot->dq_dqb.dqb_bsoftlimit &&
|
|
dquot->dq_dqb.dqb_btime == 0) {
|
|
dquot->dq_dqb.dqb_btime == 0) {
|
|
if (!prealloc) {
|
|
if (!prealloc) {
|
|
*warntype = QUOTA_NL_BSOFTWARN;
|
|
*warntype = QUOTA_NL_BSOFTWARN;
|
|
@@ -1096,10 +1168,11 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war
|
|
return QUOTA_OK;
|
|
return QUOTA_OK;
|
|
}
|
|
}
|
|
|
|
|
|
-static int info_idq_free(struct dquot *dquot, ulong inodes)
|
|
|
|
|
|
+static int info_idq_free(struct dquot *dquot, qsize_t inodes)
|
|
{
|
|
{
|
|
if (test_bit(DQ_FAKE_B, &dquot->dq_flags) ||
|
|
if (test_bit(DQ_FAKE_B, &dquot->dq_flags) ||
|
|
- dquot->dq_dqb.dqb_curinodes <= dquot->dq_dqb.dqb_isoftlimit)
|
|
|
|
|
|
+ dquot->dq_dqb.dqb_curinodes <= dquot->dq_dqb.dqb_isoftlimit ||
|
|
|
|
+ !sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_type))
|
|
return QUOTA_NL_NOWARN;
|
|
return QUOTA_NL_NOWARN;
|
|
|
|
|
|
if (dquot->dq_dqb.dqb_curinodes - inodes <= dquot->dq_dqb.dqb_isoftlimit)
|
|
if (dquot->dq_dqb.dqb_curinodes - inodes <= dquot->dq_dqb.dqb_isoftlimit)
|
|
@@ -1113,15 +1186,13 @@ static int info_idq_free(struct dquot *dquot, ulong inodes)
|
|
static int info_bdq_free(struct dquot *dquot, qsize_t space)
|
|
static int info_bdq_free(struct dquot *dquot, qsize_t space)
|
|
{
|
|
{
|
|
if (test_bit(DQ_FAKE_B, &dquot->dq_flags) ||
|
|
if (test_bit(DQ_FAKE_B, &dquot->dq_flags) ||
|
|
- toqb(dquot->dq_dqb.dqb_curspace) <= dquot->dq_dqb.dqb_bsoftlimit)
|
|
|
|
|
|
+ dquot->dq_dqb.dqb_curspace <= dquot->dq_dqb.dqb_bsoftlimit)
|
|
return QUOTA_NL_NOWARN;
|
|
return QUOTA_NL_NOWARN;
|
|
|
|
|
|
- if (toqb(dquot->dq_dqb.dqb_curspace - space) <=
|
|
|
|
- dquot->dq_dqb.dqb_bsoftlimit)
|
|
|
|
|
|
+ if (dquot->dq_dqb.dqb_curspace - space <= dquot->dq_dqb.dqb_bsoftlimit)
|
|
return QUOTA_NL_BSOFTBELOW;
|
|
return QUOTA_NL_BSOFTBELOW;
|
|
- if (toqb(dquot->dq_dqb.dqb_curspace) >= dquot->dq_dqb.dqb_bhardlimit &&
|
|
|
|
- toqb(dquot->dq_dqb.dqb_curspace - space) <
|
|
|
|
- dquot->dq_dqb.dqb_bhardlimit)
|
|
|
|
|
|
+ if (dquot->dq_dqb.dqb_curspace >= dquot->dq_dqb.dqb_bhardlimit &&
|
|
|
|
+ dquot->dq_dqb.dqb_curspace - space < dquot->dq_dqb.dqb_bhardlimit)
|
|
return QUOTA_NL_BHARDBELOW;
|
|
return QUOTA_NL_BHARDBELOW;
|
|
return QUOTA_NL_NOWARN;
|
|
return QUOTA_NL_NOWARN;
|
|
}
|
|
}
|
|
@@ -1166,17 +1237,23 @@ out_err:
|
|
* Release all quotas referenced by inode
|
|
* Release all quotas referenced by inode
|
|
* Transaction must be started at an entry
|
|
* Transaction must be started at an entry
|
|
*/
|
|
*/
|
|
-int dquot_drop(struct inode *inode)
|
|
|
|
|
|
+int dquot_drop_locked(struct inode *inode)
|
|
{
|
|
{
|
|
int cnt;
|
|
int cnt;
|
|
|
|
|
|
- down_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
|
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
|
if (inode->i_dquot[cnt] != NODQUOT) {
|
|
if (inode->i_dquot[cnt] != NODQUOT) {
|
|
dqput(inode->i_dquot[cnt]);
|
|
dqput(inode->i_dquot[cnt]);
|
|
inode->i_dquot[cnt] = NODQUOT;
|
|
inode->i_dquot[cnt] = NODQUOT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int dquot_drop(struct inode *inode)
|
|
|
|
+{
|
|
|
|
+ down_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
|
|
|
+ dquot_drop_locked(inode);
|
|
up_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
|
up_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -1264,7 +1341,7 @@ warn_put_all:
|
|
/*
|
|
/*
|
|
* This operation can block, but only after everything is updated
|
|
* This operation can block, but only after everything is updated
|
|
*/
|
|
*/
|
|
-int dquot_alloc_inode(const struct inode *inode, unsigned long number)
|
|
|
|
|
|
+int dquot_alloc_inode(const struct inode *inode, qsize_t number)
|
|
{
|
|
{
|
|
int cnt, ret = NO_QUOTA;
|
|
int cnt, ret = NO_QUOTA;
|
|
char warntype[MAXQUOTAS];
|
|
char warntype[MAXQUOTAS];
|
|
@@ -1349,7 +1426,7 @@ out_sub:
|
|
/*
|
|
/*
|
|
* This operation can block, but only after everything is updated
|
|
* This operation can block, but only after everything is updated
|
|
*/
|
|
*/
|
|
-int dquot_free_inode(const struct inode *inode, unsigned long number)
|
|
|
|
|
|
+int dquot_free_inode(const struct inode *inode, qsize_t number)
|
|
{
|
|
{
|
|
unsigned int cnt;
|
|
unsigned int cnt;
|
|
char warntype[MAXQUOTAS];
|
|
char warntype[MAXQUOTAS];
|
|
@@ -1495,7 +1572,7 @@ warn_put_all:
|
|
/* Wrapper for transferring ownership of an inode */
|
|
/* Wrapper for transferring ownership of an inode */
|
|
int vfs_dq_transfer(struct inode *inode, struct iattr *iattr)
|
|
int vfs_dq_transfer(struct inode *inode, struct iattr *iattr)
|
|
{
|
|
{
|
|
- if (sb_any_quota_enabled(inode->i_sb) && !IS_NOQUOTA(inode)) {
|
|
|
|
|
|
+ if (sb_any_quota_active(inode->i_sb) && !IS_NOQUOTA(inode)) {
|
|
vfs_dq_init(inode);
|
|
vfs_dq_init(inode);
|
|
if (inode->i_sb->dq_op->transfer(inode, iattr) == NO_QUOTA)
|
|
if (inode->i_sb->dq_op->transfer(inode, iattr) == NO_QUOTA)
|
|
return 1;
|
|
return 1;
|
|
@@ -1533,54 +1610,27 @@ struct dquot_operations dquot_operations = {
|
|
.acquire_dquot = dquot_acquire,
|
|
.acquire_dquot = dquot_acquire,
|
|
.release_dquot = dquot_release,
|
|
.release_dquot = dquot_release,
|
|
.mark_dirty = dquot_mark_dquot_dirty,
|
|
.mark_dirty = dquot_mark_dquot_dirty,
|
|
- .write_info = dquot_commit_info
|
|
|
|
|
|
+ .write_info = dquot_commit_info,
|
|
|
|
+ .alloc_dquot = dquot_alloc,
|
|
|
|
+ .destroy_dquot = dquot_destroy,
|
|
};
|
|
};
|
|
|
|
|
|
-static inline void set_enable_flags(struct quota_info *dqopt, int type)
|
|
|
|
-{
|
|
|
|
- switch (type) {
|
|
|
|
- case USRQUOTA:
|
|
|
|
- dqopt->flags |= DQUOT_USR_ENABLED;
|
|
|
|
- dqopt->flags &= ~DQUOT_USR_SUSPENDED;
|
|
|
|
- break;
|
|
|
|
- case GRPQUOTA:
|
|
|
|
- dqopt->flags |= DQUOT_GRP_ENABLED;
|
|
|
|
- dqopt->flags &= ~DQUOT_GRP_SUSPENDED;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static inline void reset_enable_flags(struct quota_info *dqopt, int type,
|
|
|
|
- int remount)
|
|
|
|
-{
|
|
|
|
- switch (type) {
|
|
|
|
- case USRQUOTA:
|
|
|
|
- dqopt->flags &= ~DQUOT_USR_ENABLED;
|
|
|
|
- if (remount)
|
|
|
|
- dqopt->flags |= DQUOT_USR_SUSPENDED;
|
|
|
|
- else
|
|
|
|
- dqopt->flags &= ~DQUOT_USR_SUSPENDED;
|
|
|
|
- break;
|
|
|
|
- case GRPQUOTA:
|
|
|
|
- dqopt->flags &= ~DQUOT_GRP_ENABLED;
|
|
|
|
- if (remount)
|
|
|
|
- dqopt->flags |= DQUOT_GRP_SUSPENDED;
|
|
|
|
- else
|
|
|
|
- dqopt->flags &= ~DQUOT_GRP_SUSPENDED;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
|
|
* Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
|
|
*/
|
|
*/
|
|
-int vfs_quota_off(struct super_block *sb, int type, int remount)
|
|
|
|
|
|
+int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags)
|
|
{
|
|
{
|
|
int cnt, ret = 0;
|
|
int cnt, ret = 0;
|
|
struct quota_info *dqopt = sb_dqopt(sb);
|
|
struct quota_info *dqopt = sb_dqopt(sb);
|
|
struct inode *toputinode[MAXQUOTAS];
|
|
struct inode *toputinode[MAXQUOTAS];
|
|
|
|
|
|
|
|
+ /* Cannot turn off usage accounting without turning off limits, or
|
|
|
|
+ * suspend quotas and simultaneously turn quotas off. */
|
|
|
|
+ if ((flags & DQUOT_USAGE_ENABLED && !(flags & DQUOT_LIMITS_ENABLED))
|
|
|
|
+ || (flags & DQUOT_SUSPENDED && flags & (DQUOT_LIMITS_ENABLED |
|
|
|
|
+ DQUOT_USAGE_ENABLED)))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
/* We need to serialize quota_off() for device */
|
|
/* We need to serialize quota_off() for device */
|
|
mutex_lock(&dqopt->dqonoff_mutex);
|
|
mutex_lock(&dqopt->dqonoff_mutex);
|
|
|
|
|
|
@@ -1589,7 +1639,7 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)
|
|
* sometimes we are called when fill_super() failed and calling
|
|
* sometimes we are called when fill_super() failed and calling
|
|
* sync_fs() in such cases does no good.
|
|
* sync_fs() in such cases does no good.
|
|
*/
|
|
*/
|
|
- if (!sb_any_quota_enabled(sb) && !sb_any_quota_suspended(sb)) {
|
|
|
|
|
|
+ if (!sb_any_quota_loaded(sb)) {
|
|
mutex_unlock(&dqopt->dqonoff_mutex);
|
|
mutex_unlock(&dqopt->dqonoff_mutex);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -1597,17 +1647,28 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)
|
|
toputinode[cnt] = NULL;
|
|
toputinode[cnt] = NULL;
|
|
if (type != -1 && cnt != type)
|
|
if (type != -1 && cnt != type)
|
|
continue;
|
|
continue;
|
|
- /* If we keep inodes of quota files after remount and quotaoff
|
|
|
|
- * is called, drop kept inodes. */
|
|
|
|
- if (!remount && sb_has_quota_suspended(sb, cnt)) {
|
|
|
|
- iput(dqopt->files[cnt]);
|
|
|
|
- dqopt->files[cnt] = NULL;
|
|
|
|
- reset_enable_flags(dqopt, cnt, 0);
|
|
|
|
|
|
+ if (!sb_has_quota_loaded(sb, cnt))
|
|
continue;
|
|
continue;
|
|
|
|
+
|
|
|
|
+ if (flags & DQUOT_SUSPENDED) {
|
|
|
|
+ dqopt->flags |=
|
|
|
|
+ dquot_state_flag(DQUOT_SUSPENDED, cnt);
|
|
|
|
+ } else {
|
|
|
|
+ dqopt->flags &= ~dquot_state_flag(flags, cnt);
|
|
|
|
+ /* Turning off suspended quotas? */
|
|
|
|
+ if (!sb_has_quota_loaded(sb, cnt) &&
|
|
|
|
+ sb_has_quota_suspended(sb, cnt)) {
|
|
|
|
+ dqopt->flags &= ~dquot_state_flag(
|
|
|
|
+ DQUOT_SUSPENDED, cnt);
|
|
|
|
+ iput(dqopt->files[cnt]);
|
|
|
|
+ dqopt->files[cnt] = NULL;
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- if (!sb_has_quota_enabled(sb, cnt))
|
|
|
|
|
|
+
|
|
|
|
+ /* We still have to keep quota loaded? */
|
|
|
|
+ if (sb_has_quota_loaded(sb, cnt) && !(flags & DQUOT_SUSPENDED))
|
|
continue;
|
|
continue;
|
|
- reset_enable_flags(dqopt, cnt, remount);
|
|
|
|
|
|
|
|
/* Note: these are blocking operations */
|
|
/* Note: these are blocking operations */
|
|
drop_dquot_ref(sb, cnt);
|
|
drop_dquot_ref(sb, cnt);
|
|
@@ -1623,7 +1684,7 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)
|
|
put_quota_format(dqopt->info[cnt].dqi_format);
|
|
put_quota_format(dqopt->info[cnt].dqi_format);
|
|
|
|
|
|
toputinode[cnt] = dqopt->files[cnt];
|
|
toputinode[cnt] = dqopt->files[cnt];
|
|
- if (!remount)
|
|
|
|
|
|
+ if (!sb_has_quota_loaded(sb, cnt))
|
|
dqopt->files[cnt] = NULL;
|
|
dqopt->files[cnt] = NULL;
|
|
dqopt->info[cnt].dqi_flags = 0;
|
|
dqopt->info[cnt].dqi_flags = 0;
|
|
dqopt->info[cnt].dqi_igrace = 0;
|
|
dqopt->info[cnt].dqi_igrace = 0;
|
|
@@ -1631,6 +1692,11 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)
|
|
dqopt->ops[cnt] = NULL;
|
|
dqopt->ops[cnt] = NULL;
|
|
}
|
|
}
|
|
mutex_unlock(&dqopt->dqonoff_mutex);
|
|
mutex_unlock(&dqopt->dqonoff_mutex);
|
|
|
|
+
|
|
|
|
+ /* Skip syncing and setting flags if quota files are hidden */
|
|
|
|
+ if (dqopt->flags & DQUOT_QUOTA_SYS_FILE)
|
|
|
|
+ goto put_inodes;
|
|
|
|
+
|
|
/* Sync the superblock so that buffers with quota data are written to
|
|
/* Sync the superblock so that buffers with quota data are written to
|
|
* disk (and so userspace sees correct data afterwards). */
|
|
* disk (and so userspace sees correct data afterwards). */
|
|
if (sb->s_op->sync_fs)
|
|
if (sb->s_op->sync_fs)
|
|
@@ -1646,7 +1712,7 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)
|
|
mutex_lock(&dqopt->dqonoff_mutex);
|
|
mutex_lock(&dqopt->dqonoff_mutex);
|
|
/* If quota was reenabled in the meantime, we have
|
|
/* If quota was reenabled in the meantime, we have
|
|
* nothing to do */
|
|
* nothing to do */
|
|
- if (!sb_has_quota_enabled(sb, cnt)) {
|
|
|
|
|
|
+ if (!sb_has_quota_loaded(sb, cnt)) {
|
|
mutex_lock_nested(&toputinode[cnt]->i_mutex, I_MUTEX_QUOTA);
|
|
mutex_lock_nested(&toputinode[cnt]->i_mutex, I_MUTEX_QUOTA);
|
|
toputinode[cnt]->i_flags &= ~(S_IMMUTABLE |
|
|
toputinode[cnt]->i_flags &= ~(S_IMMUTABLE |
|
|
S_NOATIME | S_NOQUOTA);
|
|
S_NOATIME | S_NOQUOTA);
|
|
@@ -1655,26 +1721,43 @@ int vfs_quota_off(struct super_block *sb, int type, int remount)
|
|
mark_inode_dirty(toputinode[cnt]);
|
|
mark_inode_dirty(toputinode[cnt]);
|
|
}
|
|
}
|
|
mutex_unlock(&dqopt->dqonoff_mutex);
|
|
mutex_unlock(&dqopt->dqonoff_mutex);
|
|
|
|
+ }
|
|
|
|
+ if (sb->s_bdev)
|
|
|
|
+ invalidate_bdev(sb->s_bdev);
|
|
|
|
+put_inodes:
|
|
|
|
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
|
|
|
+ if (toputinode[cnt]) {
|
|
/* On remount RO, we keep the inode pointer so that we
|
|
/* On remount RO, we keep the inode pointer so that we
|
|
- * can reenable quota on the subsequent remount RW.
|
|
|
|
- * But we have better not keep inode pointer when there
|
|
|
|
- * is pending delete on the quota file... */
|
|
|
|
- if (!remount)
|
|
|
|
|
|
+ * can reenable quota on the subsequent remount RW. We
|
|
|
|
+ * have to check 'flags' variable and not use sb_has_
|
|
|
|
+ * function because another quotaon / quotaoff could
|
|
|
|
+ * change global state before we got here. We refuse
|
|
|
|
+ * to suspend quotas when there is pending delete on
|
|
|
|
+ * the quota file... */
|
|
|
|
+ if (!(flags & DQUOT_SUSPENDED))
|
|
iput(toputinode[cnt]);
|
|
iput(toputinode[cnt]);
|
|
else if (!toputinode[cnt]->i_nlink)
|
|
else if (!toputinode[cnt]->i_nlink)
|
|
ret = -EBUSY;
|
|
ret = -EBUSY;
|
|
}
|
|
}
|
|
- if (sb->s_bdev)
|
|
|
|
- invalidate_bdev(sb->s_bdev);
|
|
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+int vfs_quota_off(struct super_block *sb, int type, int remount)
|
|
|
|
+{
|
|
|
|
+ return vfs_quota_disable(sb, type, remount ? DQUOT_SUSPENDED :
|
|
|
|
+ (DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED));
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Turn quotas on on a device
|
|
* Turn quotas on on a device
|
|
*/
|
|
*/
|
|
|
|
|
|
-/* Helper function when we already have the inode */
|
|
|
|
-static int vfs_quota_on_inode(struct inode *inode, int type, int format_id)
|
|
|
|
|
|
+/*
|
|
|
|
+ * Helper function to turn quotas on when we already have the inode of
|
|
|
|
+ * quota file and no quota information is loaded.
|
|
|
|
+ */
|
|
|
|
+static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
|
|
|
|
+ unsigned int flags)
|
|
{
|
|
{
|
|
struct quota_format_type *fmt = find_quota_format(format_id);
|
|
struct quota_format_type *fmt = find_quota_format(format_id);
|
|
struct super_block *sb = inode->i_sb;
|
|
struct super_block *sb = inode->i_sb;
|
|
@@ -1696,27 +1779,37 @@ static int vfs_quota_on_inode(struct inode *inode, int type, int format_id)
|
|
error = -EINVAL;
|
|
error = -EINVAL;
|
|
goto out_fmt;
|
|
goto out_fmt;
|
|
}
|
|
}
|
|
|
|
+ /* Usage always has to be set... */
|
|
|
|
+ if (!(flags & DQUOT_USAGE_ENABLED)) {
|
|
|
|
+ error = -EINVAL;
|
|
|
|
+ goto out_fmt;
|
|
|
|
+ }
|
|
|
|
|
|
- /* As we bypass the pagecache we must now flush the inode so that
|
|
|
|
- * we see all the changes from userspace... */
|
|
|
|
- write_inode_now(inode, 1);
|
|
|
|
- /* And now flush the block cache so that kernel sees the changes */
|
|
|
|
- invalidate_bdev(sb->s_bdev);
|
|
|
|
|
|
+ if (!(dqopt->flags & DQUOT_QUOTA_SYS_FILE)) {
|
|
|
|
+ /* As we bypass the pagecache we must now flush the inode so
|
|
|
|
+ * that we see all the changes from userspace... */
|
|
|
|
+ write_inode_now(inode, 1);
|
|
|
|
+ /* And now flush the block cache so that kernel sees the
|
|
|
|
+ * changes */
|
|
|
|
+ invalidate_bdev(sb->s_bdev);
|
|
|
|
+ }
|
|
mutex_lock(&inode->i_mutex);
|
|
mutex_lock(&inode->i_mutex);
|
|
mutex_lock(&dqopt->dqonoff_mutex);
|
|
mutex_lock(&dqopt->dqonoff_mutex);
|
|
- if (sb_has_quota_enabled(sb, type) ||
|
|
|
|
- sb_has_quota_suspended(sb, type)) {
|
|
|
|
|
|
+ if (sb_has_quota_loaded(sb, type)) {
|
|
error = -EBUSY;
|
|
error = -EBUSY;
|
|
goto out_lock;
|
|
goto out_lock;
|
|
}
|
|
}
|
|
- /* We don't want quota and atime on quota files (deadlocks possible)
|
|
|
|
- * Also nobody should write to the file - we use special IO operations
|
|
|
|
- * which ignore the immutable bit. */
|
|
|
|
- down_write(&dqopt->dqptr_sem);
|
|
|
|
- oldflags = inode->i_flags & (S_NOATIME | S_IMMUTABLE | S_NOQUOTA);
|
|
|
|
- inode->i_flags |= S_NOQUOTA | S_NOATIME | S_IMMUTABLE;
|
|
|
|
- up_write(&dqopt->dqptr_sem);
|
|
|
|
- sb->dq_op->drop(inode);
|
|
|
|
|
|
+
|
|
|
|
+ if (!(dqopt->flags & DQUOT_QUOTA_SYS_FILE)) {
|
|
|
|
+ /* We don't want quota and atime on quota files (deadlocks
|
|
|
|
+ * possible) Also nobody should write to the file - we use
|
|
|
|
+ * special IO operations which ignore the immutable bit. */
|
|
|
|
+ down_write(&dqopt->dqptr_sem);
|
|
|
|
+ oldflags = inode->i_flags & (S_NOATIME | S_IMMUTABLE | S_NOQUOTA);
|
|
|
|
+ inode->i_flags |= S_NOQUOTA | S_NOATIME | S_IMMUTABLE;
|
|
|
|
+ up_write(&dqopt->dqptr_sem);
|
|
|
|
+ sb->dq_op->drop(inode);
|
|
|
|
+ }
|
|
|
|
|
|
error = -EIO;
|
|
error = -EIO;
|
|
dqopt->files[type] = igrab(inode);
|
|
dqopt->files[type] = igrab(inode);
|
|
@@ -1737,7 +1830,7 @@ static int vfs_quota_on_inode(struct inode *inode, int type, int format_id)
|
|
}
|
|
}
|
|
mutex_unlock(&dqopt->dqio_mutex);
|
|
mutex_unlock(&dqopt->dqio_mutex);
|
|
mutex_unlock(&inode->i_mutex);
|
|
mutex_unlock(&inode->i_mutex);
|
|
- set_enable_flags(dqopt, type);
|
|
|
|
|
|
+ dqopt->flags |= dquot_state_flag(flags, type);
|
|
|
|
|
|
add_dquot_ref(sb, type);
|
|
add_dquot_ref(sb, type);
|
|
mutex_unlock(&dqopt->dqonoff_mutex);
|
|
mutex_unlock(&dqopt->dqonoff_mutex);
|
|
@@ -1770,20 +1863,23 @@ static int vfs_quota_on_remount(struct super_block *sb, int type)
|
|
struct quota_info *dqopt = sb_dqopt(sb);
|
|
struct quota_info *dqopt = sb_dqopt(sb);
|
|
struct inode *inode;
|
|
struct inode *inode;
|
|
int ret;
|
|
int ret;
|
|
|
|
+ unsigned int flags;
|
|
|
|
|
|
mutex_lock(&dqopt->dqonoff_mutex);
|
|
mutex_lock(&dqopt->dqonoff_mutex);
|
|
if (!sb_has_quota_suspended(sb, type)) {
|
|
if (!sb_has_quota_suspended(sb, type)) {
|
|
mutex_unlock(&dqopt->dqonoff_mutex);
|
|
mutex_unlock(&dqopt->dqonoff_mutex);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
- BUG_ON(sb_has_quota_enabled(sb, type));
|
|
|
|
-
|
|
|
|
inode = dqopt->files[type];
|
|
inode = dqopt->files[type];
|
|
dqopt->files[type] = NULL;
|
|
dqopt->files[type] = NULL;
|
|
- reset_enable_flags(dqopt, type, 0);
|
|
|
|
|
|
+ flags = dqopt->flags & dquot_state_flag(DQUOT_USAGE_ENABLED |
|
|
|
|
+ DQUOT_LIMITS_ENABLED, type);
|
|
|
|
+ dqopt->flags &= ~dquot_state_flag(DQUOT_STATE_FLAGS, type);
|
|
mutex_unlock(&dqopt->dqonoff_mutex);
|
|
mutex_unlock(&dqopt->dqonoff_mutex);
|
|
|
|
|
|
- ret = vfs_quota_on_inode(inode, type, dqopt->info[type].dqi_fmt_id);
|
|
|
|
|
|
+ flags = dquot_generic_flag(flags, type);
|
|
|
|
+ ret = vfs_load_quota_inode(inode, type, dqopt->info[type].dqi_fmt_id,
|
|
|
|
+ flags);
|
|
iput(inode);
|
|
iput(inode);
|
|
|
|
|
|
return ret;
|
|
return ret;
|
|
@@ -1799,12 +1895,12 @@ int vfs_quota_on_path(struct super_block *sb, int type, int format_id,
|
|
if (path->mnt->mnt_sb != sb)
|
|
if (path->mnt->mnt_sb != sb)
|
|
error = -EXDEV;
|
|
error = -EXDEV;
|
|
else
|
|
else
|
|
- error = vfs_quota_on_inode(path->dentry->d_inode, type,
|
|
|
|
- format_id);
|
|
|
|
|
|
+ error = vfs_load_quota_inode(path->dentry->d_inode, type,
|
|
|
|
+ format_id, DQUOT_USAGE_ENABLED |
|
|
|
|
+ DQUOT_LIMITS_ENABLED);
|
|
return error;
|
|
return error;
|
|
}
|
|
}
|
|
|
|
|
|
-/* Actual function called from quotactl() */
|
|
|
|
int vfs_quota_on(struct super_block *sb, int type, int format_id, char *name,
|
|
int vfs_quota_on(struct super_block *sb, int type, int format_id, char *name,
|
|
int remount)
|
|
int remount)
|
|
{
|
|
{
|
|
@@ -1822,6 +1918,50 @@ int vfs_quota_on(struct super_block *sb, int type, int format_id, char *name,
|
|
return error;
|
|
return error;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * More powerful function for turning on quotas allowing setting
|
|
|
|
+ * of individual quota flags
|
|
|
|
+ */
|
|
|
|
+int vfs_quota_enable(struct inode *inode, int type, int format_id,
|
|
|
|
+ unsigned int flags)
|
|
|
|
+{
|
|
|
|
+ int ret = 0;
|
|
|
|
+ struct super_block *sb = inode->i_sb;
|
|
|
|
+ struct quota_info *dqopt = sb_dqopt(sb);
|
|
|
|
+
|
|
|
|
+ /* Just unsuspend quotas? */
|
|
|
|
+ if (flags & DQUOT_SUSPENDED)
|
|
|
|
+ return vfs_quota_on_remount(sb, type);
|
|
|
|
+ if (!flags)
|
|
|
|
+ return 0;
|
|
|
|
+ /* Just updating flags needed? */
|
|
|
|
+ if (sb_has_quota_loaded(sb, type)) {
|
|
|
|
+ mutex_lock(&dqopt->dqonoff_mutex);
|
|
|
|
+ /* Now do a reliable test... */
|
|
|
|
+ if (!sb_has_quota_loaded(sb, type)) {
|
|
|
|
+ mutex_unlock(&dqopt->dqonoff_mutex);
|
|
|
|
+ goto load_quota;
|
|
|
|
+ }
|
|
|
|
+ if (flags & DQUOT_USAGE_ENABLED &&
|
|
|
|
+ sb_has_quota_usage_enabled(sb, type)) {
|
|
|
|
+ ret = -EBUSY;
|
|
|
|
+ goto out_lock;
|
|
|
|
+ }
|
|
|
|
+ if (flags & DQUOT_LIMITS_ENABLED &&
|
|
|
|
+ sb_has_quota_limits_enabled(sb, type)) {
|
|
|
|
+ ret = -EBUSY;
|
|
|
|
+ goto out_lock;
|
|
|
|
+ }
|
|
|
|
+ sb_dqopt(sb)->flags |= dquot_state_flag(flags, type);
|
|
|
|
+out_lock:
|
|
|
|
+ mutex_unlock(&dqopt->dqonoff_mutex);
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+load_quota:
|
|
|
|
+ return vfs_load_quota_inode(inode, type, format_id, flags);
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* This function is used when filesystem needs to initialize quotas
|
|
* This function is used when filesystem needs to initialize quotas
|
|
* during mount time.
|
|
* during mount time.
|
|
@@ -1843,7 +1983,8 @@ int vfs_quota_on_mount(struct super_block *sb, char *qf_name,
|
|
|
|
|
|
error = security_quota_on(dentry);
|
|
error = security_quota_on(dentry);
|
|
if (!error)
|
|
if (!error)
|
|
- error = vfs_quota_on_inode(dentry->d_inode, type, format_id);
|
|
|
|
|
|
+ error = vfs_load_quota_inode(dentry->d_inode, type, format_id,
|
|
|
|
+ DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED);
|
|
|
|
|
|
out:
|
|
out:
|
|
dput(dentry);
|
|
dput(dentry);
|
|
@@ -1866,14 +2007,24 @@ int vfs_dq_quota_on_remount(struct super_block *sb)
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static inline qsize_t qbtos(qsize_t blocks)
|
|
|
|
+{
|
|
|
|
+ return blocks << QIF_DQBLKSIZE_BITS;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline qsize_t stoqb(qsize_t space)
|
|
|
|
+{
|
|
|
|
+ return (space + QIF_DQBLKSIZE - 1) >> QIF_DQBLKSIZE_BITS;
|
|
|
|
+}
|
|
|
|
+
|
|
/* Generic routine for getting common part of quota structure */
|
|
/* Generic routine for getting common part of quota structure */
|
|
static void do_get_dqblk(struct dquot *dquot, struct if_dqblk *di)
|
|
static void do_get_dqblk(struct dquot *dquot, struct if_dqblk *di)
|
|
{
|
|
{
|
|
struct mem_dqblk *dm = &dquot->dq_dqb;
|
|
struct mem_dqblk *dm = &dquot->dq_dqb;
|
|
|
|
|
|
spin_lock(&dq_data_lock);
|
|
spin_lock(&dq_data_lock);
|
|
- di->dqb_bhardlimit = dm->dqb_bhardlimit;
|
|
|
|
- di->dqb_bsoftlimit = dm->dqb_bsoftlimit;
|
|
|
|
|
|
+ di->dqb_bhardlimit = stoqb(dm->dqb_bhardlimit);
|
|
|
|
+ di->dqb_bsoftlimit = stoqb(dm->dqb_bsoftlimit);
|
|
di->dqb_curspace = dm->dqb_curspace;
|
|
di->dqb_curspace = dm->dqb_curspace;
|
|
di->dqb_ihardlimit = dm->dqb_ihardlimit;
|
|
di->dqb_ihardlimit = dm->dqb_ihardlimit;
|
|
di->dqb_isoftlimit = dm->dqb_isoftlimit;
|
|
di->dqb_isoftlimit = dm->dqb_isoftlimit;
|
|
@@ -1918,28 +2069,36 @@ static int do_set_dqblk(struct dquot *dquot, struct if_dqblk *di)
|
|
if (di->dqb_valid & QIF_SPACE) {
|
|
if (di->dqb_valid & QIF_SPACE) {
|
|
dm->dqb_curspace = di->dqb_curspace;
|
|
dm->dqb_curspace = di->dqb_curspace;
|
|
check_blim = 1;
|
|
check_blim = 1;
|
|
|
|
+ __set_bit(DQ_LASTSET_B + QIF_SPACE_B, &dquot->dq_flags);
|
|
}
|
|
}
|
|
if (di->dqb_valid & QIF_BLIMITS) {
|
|
if (di->dqb_valid & QIF_BLIMITS) {
|
|
- dm->dqb_bsoftlimit = di->dqb_bsoftlimit;
|
|
|
|
- dm->dqb_bhardlimit = di->dqb_bhardlimit;
|
|
|
|
|
|
+ dm->dqb_bsoftlimit = qbtos(di->dqb_bsoftlimit);
|
|
|
|
+ dm->dqb_bhardlimit = qbtos(di->dqb_bhardlimit);
|
|
check_blim = 1;
|
|
check_blim = 1;
|
|
|
|
+ __set_bit(DQ_LASTSET_B + QIF_BLIMITS_B, &dquot->dq_flags);
|
|
}
|
|
}
|
|
if (di->dqb_valid & QIF_INODES) {
|
|
if (di->dqb_valid & QIF_INODES) {
|
|
dm->dqb_curinodes = di->dqb_curinodes;
|
|
dm->dqb_curinodes = di->dqb_curinodes;
|
|
check_ilim = 1;
|
|
check_ilim = 1;
|
|
|
|
+ __set_bit(DQ_LASTSET_B + QIF_INODES_B, &dquot->dq_flags);
|
|
}
|
|
}
|
|
if (di->dqb_valid & QIF_ILIMITS) {
|
|
if (di->dqb_valid & QIF_ILIMITS) {
|
|
dm->dqb_isoftlimit = di->dqb_isoftlimit;
|
|
dm->dqb_isoftlimit = di->dqb_isoftlimit;
|
|
dm->dqb_ihardlimit = di->dqb_ihardlimit;
|
|
dm->dqb_ihardlimit = di->dqb_ihardlimit;
|
|
check_ilim = 1;
|
|
check_ilim = 1;
|
|
|
|
+ __set_bit(DQ_LASTSET_B + QIF_ILIMITS_B, &dquot->dq_flags);
|
|
}
|
|
}
|
|
- if (di->dqb_valid & QIF_BTIME)
|
|
|
|
|
|
+ if (di->dqb_valid & QIF_BTIME) {
|
|
dm->dqb_btime = di->dqb_btime;
|
|
dm->dqb_btime = di->dqb_btime;
|
|
- if (di->dqb_valid & QIF_ITIME)
|
|
|
|
|
|
+ __set_bit(DQ_LASTSET_B + QIF_BTIME_B, &dquot->dq_flags);
|
|
|
|
+ }
|
|
|
|
+ if (di->dqb_valid & QIF_ITIME) {
|
|
dm->dqb_itime = di->dqb_itime;
|
|
dm->dqb_itime = di->dqb_itime;
|
|
|
|
+ __set_bit(DQ_LASTSET_B + QIF_ITIME_B, &dquot->dq_flags);
|
|
|
|
+ }
|
|
|
|
|
|
if (check_blim) {
|
|
if (check_blim) {
|
|
- if (!dm->dqb_bsoftlimit || toqb(dm->dqb_curspace) < dm->dqb_bsoftlimit) {
|
|
|
|
|
|
+ if (!dm->dqb_bsoftlimit || dm->dqb_curspace < dm->dqb_bsoftlimit) {
|
|
dm->dqb_btime = 0;
|
|
dm->dqb_btime = 0;
|
|
clear_bit(DQ_BLKS_B, &dquot->dq_flags);
|
|
clear_bit(DQ_BLKS_B, &dquot->dq_flags);
|
|
}
|
|
}
|
|
@@ -1970,12 +2129,14 @@ int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *d
|
|
int rc;
|
|
int rc;
|
|
|
|
|
|
mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
|
|
mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
|
|
- if (!(dquot = dqget(sb, id, type))) {
|
|
|
|
- mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
|
|
|
|
- return -ESRCH;
|
|
|
|
|
|
+ dquot = dqget(sb, id, type);
|
|
|
|
+ if (!dquot) {
|
|
|
|
+ rc = -ESRCH;
|
|
|
|
+ goto out;
|
|
}
|
|
}
|
|
rc = do_set_dqblk(dquot, di);
|
|
rc = do_set_dqblk(dquot, di);
|
|
dqput(dquot);
|
|
dqput(dquot);
|
|
|
|
+out:
|
|
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
|
|
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
|
|
return rc;
|
|
return rc;
|
|
}
|
|
}
|
|
@@ -1986,7 +2147,7 @@ int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
|
|
struct mem_dqinfo *mi;
|
|
struct mem_dqinfo *mi;
|
|
|
|
|
|
mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
|
|
mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
|
|
- if (!sb_has_quota_enabled(sb, type)) {
|
|
|
|
|
|
+ if (!sb_has_quota_active(sb, type)) {
|
|
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
|
|
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
|
|
return -ESRCH;
|
|
return -ESRCH;
|
|
}
|
|
}
|
|
@@ -2005,11 +2166,12 @@ int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
|
|
int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
|
|
int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
|
|
{
|
|
{
|
|
struct mem_dqinfo *mi;
|
|
struct mem_dqinfo *mi;
|
|
|
|
+ int err = 0;
|
|
|
|
|
|
mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
|
|
mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
|
|
- if (!sb_has_quota_enabled(sb, type)) {
|
|
|
|
- mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
|
|
|
|
- return -ESRCH;
|
|
|
|
|
|
+ if (!sb_has_quota_active(sb, type)) {
|
|
|
|
+ err = -ESRCH;
|
|
|
|
+ goto out;
|
|
}
|
|
}
|
|
mi = sb_dqopt(sb)->info + type;
|
|
mi = sb_dqopt(sb)->info + type;
|
|
spin_lock(&dq_data_lock);
|
|
spin_lock(&dq_data_lock);
|
|
@@ -2023,8 +2185,9 @@ int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
|
|
mark_info_dirty(sb, type);
|
|
mark_info_dirty(sb, type);
|
|
/* Force write to disk */
|
|
/* Force write to disk */
|
|
sb->dq_op->write_info(sb, type);
|
|
sb->dq_op->write_info(sb, type);
|
|
|
|
+out:
|
|
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
|
|
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
|
|
- return 0;
|
|
|
|
|
|
+ return err;
|
|
}
|
|
}
|
|
|
|
|
|
struct quotactl_ops vfs_quotactl_ops = {
|
|
struct quotactl_ops vfs_quotactl_ops = {
|
|
@@ -2186,10 +2349,13 @@ EXPORT_SYMBOL(register_quota_format);
|
|
EXPORT_SYMBOL(unregister_quota_format);
|
|
EXPORT_SYMBOL(unregister_quota_format);
|
|
EXPORT_SYMBOL(dqstats);
|
|
EXPORT_SYMBOL(dqstats);
|
|
EXPORT_SYMBOL(dq_data_lock);
|
|
EXPORT_SYMBOL(dq_data_lock);
|
|
|
|
+EXPORT_SYMBOL(vfs_quota_enable);
|
|
EXPORT_SYMBOL(vfs_quota_on);
|
|
EXPORT_SYMBOL(vfs_quota_on);
|
|
EXPORT_SYMBOL(vfs_quota_on_path);
|
|
EXPORT_SYMBOL(vfs_quota_on_path);
|
|
EXPORT_SYMBOL(vfs_quota_on_mount);
|
|
EXPORT_SYMBOL(vfs_quota_on_mount);
|
|
|
|
+EXPORT_SYMBOL(vfs_quota_disable);
|
|
EXPORT_SYMBOL(vfs_quota_off);
|
|
EXPORT_SYMBOL(vfs_quota_off);
|
|
|
|
+EXPORT_SYMBOL(dquot_scan_active);
|
|
EXPORT_SYMBOL(vfs_quota_sync);
|
|
EXPORT_SYMBOL(vfs_quota_sync);
|
|
EXPORT_SYMBOL(vfs_get_dqinfo);
|
|
EXPORT_SYMBOL(vfs_get_dqinfo);
|
|
EXPORT_SYMBOL(vfs_set_dqinfo);
|
|
EXPORT_SYMBOL(vfs_set_dqinfo);
|
|
@@ -2202,7 +2368,11 @@ EXPORT_SYMBOL(dquot_release);
|
|
EXPORT_SYMBOL(dquot_mark_dquot_dirty);
|
|
EXPORT_SYMBOL(dquot_mark_dquot_dirty);
|
|
EXPORT_SYMBOL(dquot_initialize);
|
|
EXPORT_SYMBOL(dquot_initialize);
|
|
EXPORT_SYMBOL(dquot_drop);
|
|
EXPORT_SYMBOL(dquot_drop);
|
|
|
|
+EXPORT_SYMBOL(dquot_drop_locked);
|
|
EXPORT_SYMBOL(vfs_dq_drop);
|
|
EXPORT_SYMBOL(vfs_dq_drop);
|
|
|
|
+EXPORT_SYMBOL(dqget);
|
|
|
|
+EXPORT_SYMBOL(dqput);
|
|
|
|
+EXPORT_SYMBOL(dquot_is_cached);
|
|
EXPORT_SYMBOL(dquot_alloc_space);
|
|
EXPORT_SYMBOL(dquot_alloc_space);
|
|
EXPORT_SYMBOL(dquot_alloc_inode);
|
|
EXPORT_SYMBOL(dquot_alloc_inode);
|
|
EXPORT_SYMBOL(dquot_free_space);
|
|
EXPORT_SYMBOL(dquot_free_space);
|