diff options
-rw-r--r-- | fs/quota/Kconfig | 8 | ||||
-rw-r--r-- | fs/quota/quota_v2.c | 165 | ||||
-rw-r--r-- | fs/quota/quotaio_v2.h | 19 | ||||
-rw-r--r-- | include/linux/quota.h | 1 |
4 files changed, 154 insertions, 39 deletions
diff --git a/fs/quota/Kconfig b/fs/quota/Kconfig index 353e78a9ebee..efc02ebb8c70 100644 --- a/fs/quota/Kconfig +++ b/fs/quota/Kconfig | |||
@@ -46,12 +46,14 @@ config QFMT_V1 | |||
46 | format say Y here. | 46 | format say Y here. |
47 | 47 | ||
48 | config QFMT_V2 | 48 | config QFMT_V2 |
49 | tristate "Quota format v2 support" | 49 | tristate "Quota format vfsv0 and vfsv1 support" |
50 | depends on QUOTA | 50 | depends on QUOTA |
51 | select QUOTA_TREE | 51 | select QUOTA_TREE |
52 | help | 52 | help |
53 | This quota format allows using quotas with 32-bit UIDs/GIDs. If you | 53 | This config option enables kernel support for vfsv0 and vfsv1 quota |
54 | need this functionality say Y here. | 54 | formats. Both these formats support 32-bit UIDs/GIDs and vfsv1 format |
55 | also supports 64-bit inode and block quota limits. If you need this | ||
56 | functionality say Y here. | ||
55 | 57 | ||
56 | config QUOTACTL | 58 | config QUOTACTL |
57 | bool | 59 | bool |
diff --git a/fs/quota/quota_v2.c b/fs/quota/quota_v2.c index 01f25eae684d..3dfc23e02135 100644 --- a/fs/quota/quota_v2.c +++ b/fs/quota/quota_v2.c | |||
@@ -23,14 +23,23 @@ MODULE_LICENSE("GPL"); | |||
23 | 23 | ||
24 | #define __QUOTA_V2_PARANOIA | 24 | #define __QUOTA_V2_PARANOIA |
25 | 25 | ||
26 | static void v2_mem2diskdqb(void *dp, struct dquot *dquot); | 26 | static void v2r0_mem2diskdqb(void *dp, struct dquot *dquot); |
27 | static void v2_disk2memdqb(struct dquot *dquot, void *dp); | 27 | static void v2r0_disk2memdqb(struct dquot *dquot, void *dp); |
28 | static int v2_is_id(void *dp, struct dquot *dquot); | 28 | static int v2r0_is_id(void *dp, struct dquot *dquot); |
29 | 29 | static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot); | |
30 | static struct qtree_fmt_operations v2_qtree_ops = { | 30 | static void v2r1_disk2memdqb(struct dquot *dquot, void *dp); |
31 | .mem2disk_dqblk = v2_mem2diskdqb, | 31 | static int v2r1_is_id(void *dp, struct dquot *dquot); |
32 | .disk2mem_dqblk = v2_disk2memdqb, | 32 | |
33 | .is_id = v2_is_id, | 33 | static struct qtree_fmt_operations v2r0_qtree_ops = { |
34 | .mem2disk_dqblk = v2r0_mem2diskdqb, | ||
35 | .disk2mem_dqblk = v2r0_disk2memdqb, | ||
36 | .is_id = v2r0_is_id, | ||
37 | }; | ||
38 | |||
39 | static struct qtree_fmt_operations v2r1_qtree_ops = { | ||
40 | .mem2disk_dqblk = v2r1_mem2diskdqb, | ||
41 | .disk2mem_dqblk = v2r1_disk2memdqb, | ||
42 | .is_id = v2r1_is_id, | ||
34 | }; | 43 | }; |
35 | 44 | ||
36 | #define QUOTABLOCK_BITS 10 | 45 | #define QUOTABLOCK_BITS 10 |
@@ -46,23 +55,33 @@ static inline qsize_t v2_qbtos(qsize_t blocks) | |||
46 | return blocks << QUOTABLOCK_BITS; | 55 | return blocks << QUOTABLOCK_BITS; |
47 | } | 56 | } |
48 | 57 | ||
58 | static int v2_read_header(struct super_block *sb, int type, | ||
59 | struct v2_disk_dqheader *dqhead) | ||
60 | { | ||
61 | ssize_t size; | ||
62 | |||
63 | size = sb->s_op->quota_read(sb, type, (char *)dqhead, | ||
64 | sizeof(struct v2_disk_dqheader), 0); | ||
65 | if (size != sizeof(struct v2_disk_dqheader)) { | ||
66 | printk(KERN_WARNING "quota_v2: Failed header read:" | ||
67 | " expected=%zd got=%zd\n", | ||
68 | sizeof(struct v2_disk_dqheader), size); | ||
69 | return 0; | ||
70 | } | ||
71 | return 1; | ||
72 | } | ||
73 | |||
49 | /* Check whether given file is really vfsv0 quotafile */ | 74 | /* Check whether given file is really vfsv0 quotafile */ |
50 | static int v2_check_quota_file(struct super_block *sb, int type) | 75 | static int v2_check_quota_file(struct super_block *sb, int type) |
51 | { | 76 | { |
52 | struct v2_disk_dqheader dqhead; | 77 | struct v2_disk_dqheader dqhead; |
53 | ssize_t size; | ||
54 | static const uint quota_magics[] = V2_INITQMAGICS; | 78 | static const uint quota_magics[] = V2_INITQMAGICS; |
55 | static const uint quota_versions[] = V2_INITQVERSIONS; | 79 | static const uint quota_versions[] = V2_INITQVERSIONS; |
56 | 80 | ||
57 | size = sb->s_op->quota_read(sb, type, (char *)&dqhead, | 81 | if (!v2_read_header(sb, type, &dqhead)) |
58 | sizeof(struct v2_disk_dqheader), 0); | ||
59 | if (size != sizeof(struct v2_disk_dqheader)) { | ||
60 | printk("quota_v2: failed read expected=%zd got=%zd\n", | ||
61 | sizeof(struct v2_disk_dqheader), size); | ||
62 | return 0; | 82 | return 0; |
63 | } | ||
64 | if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] || | 83 | if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] || |
65 | le32_to_cpu(dqhead.dqh_version) != quota_versions[type]) | 84 | le32_to_cpu(dqhead.dqh_version) > quota_versions[type]) |
66 | return 0; | 85 | return 0; |
67 | return 1; | 86 | return 1; |
68 | } | 87 | } |
@@ -71,14 +90,20 @@ static int v2_check_quota_file(struct super_block *sb, int type) | |||
71 | static int v2_read_file_info(struct super_block *sb, int type) | 90 | static int v2_read_file_info(struct super_block *sb, int type) |
72 | { | 91 | { |
73 | struct v2_disk_dqinfo dinfo; | 92 | struct v2_disk_dqinfo dinfo; |
93 | struct v2_disk_dqheader dqhead; | ||
74 | struct mem_dqinfo *info = sb_dqinfo(sb, type); | 94 | struct mem_dqinfo *info = sb_dqinfo(sb, type); |
75 | struct qtree_mem_dqinfo *qinfo; | 95 | struct qtree_mem_dqinfo *qinfo; |
76 | ssize_t size; | 96 | ssize_t size; |
97 | unsigned int version; | ||
98 | |||
99 | if (!v2_read_header(sb, type, &dqhead)) | ||
100 | return 0; | ||
101 | version = le32_to_cpu(dqhead.dqh_version); | ||
77 | 102 | ||
78 | size = sb->s_op->quota_read(sb, type, (char *)&dinfo, | 103 | size = sb->s_op->quota_read(sb, type, (char *)&dinfo, |
79 | sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF); | 104 | sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF); |
80 | if (size != sizeof(struct v2_disk_dqinfo)) { | 105 | if (size != sizeof(struct v2_disk_dqinfo)) { |
81 | printk(KERN_WARNING "Can't read info structure on device %s.\n", | 106 | printk(KERN_WARNING "quota_v2: Can't read info structure on device %s.\n", |
82 | sb->s_id); | 107 | sb->s_id); |
83 | return -1; | 108 | return -1; |
84 | } | 109 | } |
@@ -89,9 +114,15 @@ static int v2_read_file_info(struct super_block *sb, int type) | |||
89 | return -1; | 114 | return -1; |
90 | } | 115 | } |
91 | qinfo = info->dqi_priv; | 116 | qinfo = info->dqi_priv; |
92 | /* limits are stored as unsigned 32-bit data */ | 117 | if (version == 0) { |
93 | info->dqi_maxblimit = 0xffffffff; | 118 | /* limits are stored as unsigned 32-bit data */ |
94 | info->dqi_maxilimit = 0xffffffff; | 119 | info->dqi_maxblimit = 0xffffffff; |
120 | info->dqi_maxilimit = 0xffffffff; | ||
121 | } else { | ||
122 | /* used space is stored as unsigned 64-bit value */ | ||
123 | info->dqi_maxblimit = 0xffffffffffffffff; /* 2^64-1 */ | ||
124 | info->dqi_maxilimit = 0xffffffffffffffff; | ||
125 | } | ||
95 | info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace); | 126 | info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace); |
96 | info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace); | 127 | info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace); |
97 | info->dqi_flags = le32_to_cpu(dinfo.dqi_flags); | 128 | info->dqi_flags = le32_to_cpu(dinfo.dqi_flags); |
@@ -103,8 +134,13 @@ static int v2_read_file_info(struct super_block *sb, int type) | |||
103 | qinfo->dqi_blocksize_bits = V2_DQBLKSIZE_BITS; | 134 | qinfo->dqi_blocksize_bits = V2_DQBLKSIZE_BITS; |
104 | qinfo->dqi_usable_bs = 1 << V2_DQBLKSIZE_BITS; | 135 | qinfo->dqi_usable_bs = 1 << V2_DQBLKSIZE_BITS; |
105 | qinfo->dqi_qtree_depth = qtree_depth(qinfo); | 136 | qinfo->dqi_qtree_depth = qtree_depth(qinfo); |
106 | qinfo->dqi_entry_size = sizeof(struct v2_disk_dqblk); | 137 | if (version == 0) { |
107 | qinfo->dqi_ops = &v2_qtree_ops; | 138 | qinfo->dqi_entry_size = sizeof(struct v2r0_disk_dqblk); |
139 | qinfo->dqi_ops = &v2r0_qtree_ops; | ||
140 | } else { | ||
141 | qinfo->dqi_entry_size = sizeof(struct v2r1_disk_dqblk); | ||
142 | qinfo->dqi_ops = &v2r1_qtree_ops; | ||
143 | } | ||
108 | return 0; | 144 | return 0; |
109 | } | 145 | } |
110 | 146 | ||
@@ -135,9 +171,9 @@ static int v2_write_file_info(struct super_block *sb, int type) | |||
135 | return 0; | 171 | return 0; |
136 | } | 172 | } |
137 | 173 | ||
138 | static void v2_disk2memdqb(struct dquot *dquot, void *dp) | 174 | static void v2r0_disk2memdqb(struct dquot *dquot, void *dp) |
139 | { | 175 | { |
140 | struct v2_disk_dqblk *d = dp, empty; | 176 | struct v2r0_disk_dqblk *d = dp, empty; |
141 | struct mem_dqblk *m = &dquot->dq_dqb; | 177 | struct mem_dqblk *m = &dquot->dq_dqb; |
142 | 178 | ||
143 | m->dqb_ihardlimit = le32_to_cpu(d->dqb_ihardlimit); | 179 | m->dqb_ihardlimit = le32_to_cpu(d->dqb_ihardlimit); |
@@ -149,15 +185,15 @@ static void v2_disk2memdqb(struct dquot *dquot, void *dp) | |||
149 | m->dqb_curspace = le64_to_cpu(d->dqb_curspace); | 185 | m->dqb_curspace = le64_to_cpu(d->dqb_curspace); |
150 | m->dqb_btime = le64_to_cpu(d->dqb_btime); | 186 | m->dqb_btime = le64_to_cpu(d->dqb_btime); |
151 | /* We need to escape back all-zero structure */ | 187 | /* We need to escape back all-zero structure */ |
152 | memset(&empty, 0, sizeof(struct v2_disk_dqblk)); | 188 | memset(&empty, 0, sizeof(struct v2r0_disk_dqblk)); |
153 | empty.dqb_itime = cpu_to_le64(1); | 189 | empty.dqb_itime = cpu_to_le64(1); |
154 | if (!memcmp(&empty, dp, sizeof(struct v2_disk_dqblk))) | 190 | if (!memcmp(&empty, dp, sizeof(struct v2r0_disk_dqblk))) |
155 | m->dqb_itime = 0; | 191 | m->dqb_itime = 0; |
156 | } | 192 | } |
157 | 193 | ||
158 | static void v2_mem2diskdqb(void *dp, struct dquot *dquot) | 194 | static void v2r0_mem2diskdqb(void *dp, struct dquot *dquot) |
159 | { | 195 | { |
160 | struct v2_disk_dqblk *d = dp; | 196 | struct v2r0_disk_dqblk *d = dp; |
161 | struct mem_dqblk *m = &dquot->dq_dqb; | 197 | struct mem_dqblk *m = &dquot->dq_dqb; |
162 | struct qtree_mem_dqinfo *info = | 198 | struct qtree_mem_dqinfo *info = |
163 | sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv; | 199 | sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv; |
@@ -175,9 +211,60 @@ static void v2_mem2diskdqb(void *dp, struct dquot *dquot) | |||
175 | d->dqb_itime = cpu_to_le64(1); | 211 | d->dqb_itime = cpu_to_le64(1); |
176 | } | 212 | } |
177 | 213 | ||
178 | static int v2_is_id(void *dp, struct dquot *dquot) | 214 | static int v2r0_is_id(void *dp, struct dquot *dquot) |
215 | { | ||
216 | struct v2r0_disk_dqblk *d = dp; | ||
217 | struct qtree_mem_dqinfo *info = | ||
218 | sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv; | ||
219 | |||
220 | if (qtree_entry_unused(info, dp)) | ||
221 | return 0; | ||
222 | return le32_to_cpu(d->dqb_id) == dquot->dq_id; | ||
223 | } | ||
224 | |||
225 | static void v2r1_disk2memdqb(struct dquot *dquot, void *dp) | ||
226 | { | ||
227 | struct v2r1_disk_dqblk *d = dp, empty; | ||
228 | struct mem_dqblk *m = &dquot->dq_dqb; | ||
229 | |||
230 | m->dqb_ihardlimit = le64_to_cpu(d->dqb_ihardlimit); | ||
231 | m->dqb_isoftlimit = le64_to_cpu(d->dqb_isoftlimit); | ||
232 | m->dqb_curinodes = le64_to_cpu(d->dqb_curinodes); | ||
233 | m->dqb_itime = le64_to_cpu(d->dqb_itime); | ||
234 | m->dqb_bhardlimit = v2_qbtos(le64_to_cpu(d->dqb_bhardlimit)); | ||
235 | m->dqb_bsoftlimit = v2_qbtos(le64_to_cpu(d->dqb_bsoftlimit)); | ||
236 | m->dqb_curspace = le64_to_cpu(d->dqb_curspace); | ||
237 | m->dqb_btime = le64_to_cpu(d->dqb_btime); | ||
238 | /* We need to escape back all-zero structure */ | ||
239 | memset(&empty, 0, sizeof(struct v2r1_disk_dqblk)); | ||
240 | empty.dqb_itime = cpu_to_le64(1); | ||
241 | if (!memcmp(&empty, dp, sizeof(struct v2r1_disk_dqblk))) | ||
242 | m->dqb_itime = 0; | ||
243 | } | ||
244 | |||
245 | static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot) | ||
246 | { | ||
247 | struct v2r1_disk_dqblk *d = dp; | ||
248 | struct mem_dqblk *m = &dquot->dq_dqb; | ||
249 | struct qtree_mem_dqinfo *info = | ||
250 | sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv; | ||
251 | |||
252 | d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit); | ||
253 | d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit); | ||
254 | d->dqb_curinodes = cpu_to_le64(m->dqb_curinodes); | ||
255 | d->dqb_itime = cpu_to_le64(m->dqb_itime); | ||
256 | d->dqb_bhardlimit = cpu_to_le64(v2_stoqb(m->dqb_bhardlimit)); | ||
257 | d->dqb_bsoftlimit = cpu_to_le64(v2_stoqb(m->dqb_bsoftlimit)); | ||
258 | d->dqb_curspace = cpu_to_le64(m->dqb_curspace); | ||
259 | d->dqb_btime = cpu_to_le64(m->dqb_btime); | ||
260 | d->dqb_id = cpu_to_le32(dquot->dq_id); | ||
261 | if (qtree_entry_unused(info, dp)) | ||
262 | d->dqb_itime = cpu_to_le64(1); | ||
263 | } | ||
264 | |||
265 | static int v2r1_is_id(void *dp, struct dquot *dquot) | ||
179 | { | 266 | { |
180 | struct v2_disk_dqblk *d = dp; | 267 | struct v2r1_disk_dqblk *d = dp; |
181 | struct qtree_mem_dqinfo *info = | 268 | struct qtree_mem_dqinfo *info = |
182 | sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv; | 269 | sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv; |
183 | 270 | ||
@@ -217,20 +304,32 @@ static const struct quota_format_ops v2_format_ops = { | |||
217 | .release_dqblk = v2_release_dquot, | 304 | .release_dqblk = v2_release_dquot, |
218 | }; | 305 | }; |
219 | 306 | ||
220 | static struct quota_format_type v2_quota_format = { | 307 | static struct quota_format_type v2r0_quota_format = { |
221 | .qf_fmt_id = QFMT_VFS_V0, | 308 | .qf_fmt_id = QFMT_VFS_V0, |
222 | .qf_ops = &v2_format_ops, | 309 | .qf_ops = &v2_format_ops, |
223 | .qf_owner = THIS_MODULE | 310 | .qf_owner = THIS_MODULE |
224 | }; | 311 | }; |
225 | 312 | ||
313 | static struct quota_format_type v2r1_quota_format = { | ||
314 | .qf_fmt_id = QFMT_VFS_V1, | ||
315 | .qf_ops = &v2_format_ops, | ||
316 | .qf_owner = THIS_MODULE | ||
317 | }; | ||
318 | |||
226 | static int __init init_v2_quota_format(void) | 319 | static int __init init_v2_quota_format(void) |
227 | { | 320 | { |
228 | return register_quota_format(&v2_quota_format); | 321 | int ret; |
322 | |||
323 | ret = register_quota_format(&v2r0_quota_format); | ||
324 | if (ret) | ||
325 | return ret; | ||
326 | return register_quota_format(&v2r1_quota_format); | ||
229 | } | 327 | } |
230 | 328 | ||
231 | static void __exit exit_v2_quota_format(void) | 329 | static void __exit exit_v2_quota_format(void) |
232 | { | 330 | { |
233 | unregister_quota_format(&v2_quota_format); | 331 | unregister_quota_format(&v2r0_quota_format); |
332 | unregister_quota_format(&v2r1_quota_format); | ||
234 | } | 333 | } |
235 | 334 | ||
236 | module_init(init_v2_quota_format); | 335 | module_init(init_v2_quota_format); |
diff --git a/fs/quota/quotaio_v2.h b/fs/quota/quotaio_v2.h index 530fe580685c..f1966b42c2fd 100644 --- a/fs/quota/quotaio_v2.h +++ b/fs/quota/quotaio_v2.h | |||
@@ -17,8 +17,8 @@ | |||
17 | } | 17 | } |
18 | 18 | ||
19 | #define V2_INITQVERSIONS {\ | 19 | #define V2_INITQVERSIONS {\ |
20 | 0, /* USRQUOTA */\ | 20 | 1, /* USRQUOTA */\ |
21 | 0 /* GRPQUOTA */\ | 21 | 1 /* GRPQUOTA */\ |
22 | } | 22 | } |
23 | 23 | ||
24 | /* First generic header */ | 24 | /* First generic header */ |
@@ -32,7 +32,7 @@ struct v2_disk_dqheader { | |||
32 | * (as it appears on disk) - the file is a radix tree whose leaves point | 32 | * (as it appears on disk) - the file is a radix tree whose leaves point |
33 | * to blocks of these structures. | 33 | * to blocks of these structures. |
34 | */ | 34 | */ |
35 | struct v2_disk_dqblk { | 35 | struct v2r0_disk_dqblk { |
36 | __le32 dqb_id; /* id this quota applies to */ | 36 | __le32 dqb_id; /* id this quota applies to */ |
37 | __le32 dqb_ihardlimit; /* absolute limit on allocated inodes */ | 37 | __le32 dqb_ihardlimit; /* absolute limit on allocated inodes */ |
38 | __le32 dqb_isoftlimit; /* preferred inode limit */ | 38 | __le32 dqb_isoftlimit; /* preferred inode limit */ |
@@ -44,6 +44,19 @@ struct v2_disk_dqblk { | |||
44 | __le64 dqb_itime; /* time limit for excessive inode use */ | 44 | __le64 dqb_itime; /* time limit for excessive inode use */ |
45 | }; | 45 | }; |
46 | 46 | ||
47 | struct v2r1_disk_dqblk { | ||
48 | __le32 dqb_id; /* id this quota applies to */ | ||
49 | __le32 dqb_pad; | ||
50 | __le64 dqb_ihardlimit; /* absolute limit on allocated inodes */ | ||
51 | __le64 dqb_isoftlimit; /* preferred inode limit */ | ||
52 | __le64 dqb_curinodes; /* current # allocated inodes */ | ||
53 | __le64 dqb_bhardlimit; /* absolute limit on disk space (in QUOTABLOCK_SIZE) */ | ||
54 | __le64 dqb_bsoftlimit; /* preferred limit on disk space (in QUOTABLOCK_SIZE) */ | ||
55 | __le64 dqb_curspace; /* current space occupied (in bytes) */ | ||
56 | __le64 dqb_btime; /* time limit for excessive disk use */ | ||
57 | __le64 dqb_itime; /* time limit for excessive inode use */ | ||
58 | }; | ||
59 | |||
47 | /* Header with type and version specific information */ | 60 | /* Header with type and version specific information */ |
48 | struct v2_disk_dqinfo { | 61 | struct v2_disk_dqinfo { |
49 | __le32 dqi_bgrace; /* Time before block soft limit becomes hard limit */ | 62 | __le32 dqi_bgrace; /* Time before block soft limit becomes hard limit */ |
diff --git a/include/linux/quota.h b/include/linux/quota.h index 7db3a005483f..e70e62194243 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h | |||
@@ -74,6 +74,7 @@ | |||
74 | #define QFMT_VFS_OLD 1 | 74 | #define QFMT_VFS_OLD 1 |
75 | #define QFMT_VFS_V0 2 | 75 | #define QFMT_VFS_V0 2 |
76 | #define QFMT_OCFS2 3 | 76 | #define QFMT_OCFS2 3 |
77 | #define QFMT_VFS_V1 4 | ||
77 | 78 | ||
78 | /* Size of block in which space limits are passed through the quota | 79 | /* Size of block in which space limits are passed through the quota |
79 | * interface */ | 80 | * interface */ |