2 * Implementation of new quotafile format
4 * Jan Kara <jack@suse.cz> - sponsored by SuSE CR
15 #include <asm/byteorder.h>
19 #include "quota_tree.h"
22 #include "quotaio_generic.h"
24 typedef char *dqbuf_t;
26 #define getdqbuf() smalloc(QT_BLKSIZE)
27 #define freedqbuf(buf) free(buf)
29 /* Is given dquot empty? */
30 int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk)
34 for (i = 0; i < info->dqi_entry_size; i++)
40 int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info)
42 return (QT_BLKSIZE - sizeof(struct qt_disk_dqdbheader)) / info->dqi_entry_size;
45 static int get_index(qid_t id, int depth)
47 return (id >> ((QT_TREEDEPTH - depth - 1) * 8)) & 0xff;
50 /* Read given block */
51 static void read_blk(struct quota_handle *h, uint blk, dqbuf_t buf)
55 lseek(h->qh_fd, blk << QT_BLKSIZE_BITS, SEEK_SET);
56 err = read(h->qh_fd, buf, QT_BLKSIZE);
58 die(2, _("Cannot read block %u: %s\n"), blk, strerror(errno));
59 else if (err != QT_BLKSIZE)
60 memset(buf + err, 0, QT_BLKSIZE - err);
64 static int write_blk(struct quota_handle *h, uint blk, dqbuf_t buf)
68 lseek(h->qh_fd, blk << QT_BLKSIZE_BITS, SEEK_SET);
69 err = write(h->qh_fd, buf, QT_BLKSIZE);
70 if (err < 0 && errno != ENOSPC)
71 die(2, _("Cannot write block (%u): %s\n"), blk, strerror(errno));
72 if (err != QT_BLKSIZE)
77 /* Get free block in file (either from free list or create new one) */
78 static int get_free_dqblk(struct quota_handle *h)
80 dqbuf_t buf = getdqbuf();
81 struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
82 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
85 if (info->dqi_free_blk) {
86 blk = info->dqi_free_blk;
87 read_blk(h, blk, buf);
88 info->dqi_free_blk = __le32_to_cpu(dh->dqdh_next_free);
91 memset(buf, 0, QT_BLKSIZE);
92 if (write_blk(h, info->dqi_blocks, buf) < 0) { /* Assure block allocation... */
94 errstr(_("Cannot allocate new quota block (out of disk space).\n"));
97 blk = info->dqi_blocks++;
99 mark_quotafile_info_dirty(h);
104 /* Put given block to free list */
105 static void put_free_dqblk(struct quota_handle *h, dqbuf_t buf, uint blk)
107 struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
108 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
110 dh->dqdh_next_free = __cpu_to_le32(info->dqi_free_blk);
111 dh->dqdh_prev_free = __cpu_to_le32(0);
112 dh->dqdh_entries = __cpu_to_le16(0);
113 info->dqi_free_blk = blk;
114 mark_quotafile_info_dirty(h);
115 write_blk(h, blk, buf);
118 /* Remove given block from the list of blocks with free entries */
119 static void remove_free_dqentry(struct quota_handle *h, dqbuf_t buf, uint blk)
121 dqbuf_t tmpbuf = getdqbuf();
122 struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
123 uint nextblk = __le32_to_cpu(dh->dqdh_next_free), prevblk =
125 __le32_to_cpu(dh->dqdh_prev_free);
128 read_blk(h, nextblk, tmpbuf);
129 ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = dh->dqdh_prev_free;
130 write_blk(h, nextblk, tmpbuf);
133 read_blk(h, prevblk, tmpbuf);
134 ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_next_free = dh->dqdh_next_free;
135 write_blk(h, prevblk, tmpbuf);
138 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_entry = nextblk;
139 mark_quotafile_info_dirty(h);
142 dh->dqdh_next_free = dh->dqdh_prev_free = __cpu_to_le32(0);
143 write_blk(h, blk, buf); /* No matter whether write succeeds block is out of list */
146 /* Insert given block to the beginning of list with free entries */
147 static void insert_free_dqentry(struct quota_handle *h, dqbuf_t buf, uint blk)
149 dqbuf_t tmpbuf = getdqbuf();
150 struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
151 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
153 dh->dqdh_next_free = __cpu_to_le32(info->dqi_free_entry);
154 dh->dqdh_prev_free = __cpu_to_le32(0);
155 write_blk(h, blk, buf);
156 if (info->dqi_free_entry) {
157 read_blk(h, info->dqi_free_entry, tmpbuf);
158 ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = __cpu_to_le32(blk);
159 write_blk(h, info->dqi_free_entry, tmpbuf);
162 info->dqi_free_entry = blk;
163 mark_quotafile_info_dirty(h);
166 /* Find space for dquot */
167 static uint find_free_dqentry(struct quota_handle *h, struct dquot *dquot, int *err)
170 struct qt_disk_dqdbheader *dh;
171 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
177 dh = (struct qt_disk_dqdbheader *)buf;
178 if (info->dqi_free_entry) {
179 blk = info->dqi_free_entry;
180 read_blk(h, blk, buf);
183 blk = get_free_dqblk(h);
189 memset(buf, 0, QT_BLKSIZE);
190 info->dqi_free_entry = blk;
191 mark_quotafile_info_dirty(h);
193 if (__le16_to_cpu(dh->dqdh_entries) + 1 >= qtree_dqstr_in_blk(info)) /* Block will be full? */
194 remove_free_dqentry(h, buf, blk);
195 dh->dqdh_entries = __cpu_to_le16(__le16_to_cpu(dh->dqdh_entries) + 1);
196 /* Find free structure in block */
197 ddquot = buf + sizeof(struct qt_disk_dqdbheader);
199 i < qtree_dqstr_in_blk(info) && !qtree_entry_unused(info, ddquot);
200 i++, ddquot += info->dqi_entry_size);
201 if (i == qtree_dqstr_in_blk(info))
202 die(2, _("find_free_dqentry(): Data block full but it shouldn't.\n"));
203 write_blk(h, blk, buf);
204 dquot->dq_dqb.u.v2_mdqb.dqb_off =
205 (blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) +
206 i * info->dqi_entry_size;
211 /* Insert reference to structure into the trie */
212 static int do_insert_tree(struct quota_handle *h, struct dquot *dquot, uint * treeblk, int depth)
215 int newson = 0, newact = 0;
222 ret = get_free_dqblk(h);
226 memset(buf, 0, QT_BLKSIZE);
230 read_blk(h, *treeblk, buf);
231 ref = (u_int32_t *) buf;
232 newblk = __le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
235 if (depth == QT_TREEDEPTH - 1) {
237 die(2, _("Inserting already present quota entry (block %u).\n"),
238 ref[get_index(dquot->dq_id, depth)]);
239 newblk = find_free_dqentry(h, dquot, &ret);
242 ret = do_insert_tree(h, dquot, &newblk, depth + 1);
243 if (newson && ret >= 0) {
244 ref[get_index(dquot->dq_id, depth)] = __cpu_to_le32(newblk);
245 write_blk(h, *treeblk, buf);
247 else if (newact && ret < 0)
248 put_free_dqblk(h, buf, *treeblk);
254 /* Wrapper for inserting quota structure into tree */
255 static void dq_insert_tree(struct quota_handle *h, struct dquot *dquot)
257 uint tmp = QT_TREEOFF;
259 if (do_insert_tree(h, dquot, &tmp, 0) < 0)
260 die(2, _("Cannot write quota (id %u): %s\n"), (uint) dquot->dq_id, strerror(errno));
263 /* Write dquot to file */
264 void qtree_write_dquot(struct dquot *dquot)
267 struct qtree_mem_dqinfo *info = &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
268 char *ddquot = smalloc(info->dqi_entry_size);
270 if (!dquot->dq_dqb.u.v2_mdqb.dqb_off)
271 dq_insert_tree(dquot->dq_h, dquot);
272 lseek(dquot->dq_h->qh_fd, dquot->dq_dqb.u.v2_mdqb.dqb_off, SEEK_SET);
273 info->dqi_ops->mem2disk_dqblk(ddquot, dquot);
274 ret = write(dquot->dq_h->qh_fd, ddquot, info->dqi_entry_size);
275 if (ret != info->dqi_entry_size) {
278 die(2, _("Quota write failed (id %u): %s\n"), (uint)dquot->dq_id, strerror(errno));
282 /* Free dquot entry in data block */
283 static void free_dqentry(struct quota_handle *h, struct dquot *dquot, uint blk)
285 struct qt_disk_dqdbheader *dh;
286 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
287 dqbuf_t buf = getdqbuf();
289 if (dquot->dq_dqb.u.v2_mdqb.dqb_off >> QT_BLKSIZE_BITS != blk)
290 die(2, _("Quota structure has offset to other block (%u) than it should (%u).\n"), blk,
291 (uint) (dquot->dq_dqb.u.v2_mdqb.dqb_off >> QT_BLKSIZE_BITS));
292 read_blk(h, blk, buf);
293 dh = (struct qt_disk_dqdbheader *)buf;
294 dh->dqdh_entries = __cpu_to_le16(__le16_to_cpu(dh->dqdh_entries) - 1);
295 if (!__le16_to_cpu(dh->dqdh_entries)) { /* Block got free? */
296 remove_free_dqentry(h, buf, blk);
297 put_free_dqblk(h, buf, blk);
300 memset(buf + (dquot->dq_dqb.u.v2_mdqb.dqb_off & ((1 << QT_BLKSIZE_BITS) - 1)), 0,
301 info->dqi_entry_size);
303 if (__le16_to_cpu(dh->dqdh_entries) == qtree_dqstr_in_blk(info) - 1) /* First free entry? */
304 insert_free_dqentry(h, buf, blk); /* This will also write data block */
306 write_blk(h, blk, buf);
308 dquot->dq_dqb.u.v2_mdqb.dqb_off = 0;
312 /* Remove reference to dquot from tree */
313 static void remove_tree(struct quota_handle *h, struct dquot *dquot, uint * blk, int depth)
315 dqbuf_t buf = getdqbuf();
317 u_int32_t *ref = (u_int32_t *) buf;
319 read_blk(h, *blk, buf);
320 newblk = __le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
321 if (depth == QT_TREEDEPTH - 1) {
322 free_dqentry(h, dquot, newblk);
326 remove_tree(h, dquot, &newblk, depth + 1);
330 ref[get_index(dquot->dq_id, depth)] = __cpu_to_le32(0);
331 for (i = 0; i < QT_BLKSIZE && !buf[i]; i++); /* Block got empty? */
332 /* Don't put the root block into the free block list */
333 if (i == QT_BLKSIZE && *blk != QT_TREEOFF) {
334 put_free_dqblk(h, buf, *blk);
338 write_blk(h, *blk, buf);
343 /* Delete dquot from tree */
344 void qtree_delete_dquot(struct dquot *dquot)
346 uint tmp = QT_TREEOFF;
348 if (!dquot->dq_dqb.u.v2_mdqb.dqb_off) /* Even not allocated? */
350 remove_tree(dquot->dq_h, dquot, &tmp, 0);
353 /* Find entry in block */
354 static loff_t find_block_dqentry(struct quota_handle *h, struct dquot *dquot, uint blk)
356 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
357 dqbuf_t buf = getdqbuf();
359 char *ddquot = buf + sizeof(struct qt_disk_dqdbheader);
361 read_blk(h, blk, buf);
363 i < qtree_dqstr_in_blk(info) && !info->dqi_ops->is_id(ddquot, dquot);
364 i++, ddquot += info->dqi_entry_size);
365 if (i == qtree_dqstr_in_blk(info))
366 die(2, _("Quota for id %u referenced but not present.\n"), dquot->dq_id);
368 return (blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) +
369 i * info->dqi_entry_size;
372 /* Find entry for given id in the tree */
373 static loff_t find_tree_dqentry(struct quota_handle *h, struct dquot *dquot, uint blk, int depth)
375 dqbuf_t buf = getdqbuf();
377 u_int32_t *ref = (u_int32_t *) buf;
379 read_blk(h, blk, buf);
381 blk = __le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
382 if (!blk) /* No reference? */
384 if (depth < QT_TREEDEPTH - 1)
385 ret = find_tree_dqentry(h, dquot, blk, depth + 1);
387 ret = find_block_dqentry(h, dquot, blk);
393 /* Find entry for given id in the tree - wrapper function */
394 static inline loff_t find_dqentry(struct quota_handle *h, struct dquot *dquot)
396 return find_tree_dqentry(h, dquot, QT_TREEOFF, 0);
400 * Read dquot (either from disk or from kernel)
401 * User can use errno to detect errstr when NULL is returned
403 struct dquot *qtree_read_dquot(struct quota_handle *h, qid_t id)
405 struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
408 char *ddquot = smalloc(info->dqi_entry_size);
409 struct dquot *dquot = get_empty_dquot();
413 dquot->dq_dqb.u.v2_mdqb.dqb_off = 0;
414 memset(&dquot->dq_dqb, 0, sizeof(struct util_dqblk));
416 offset = find_dqentry(h, dquot);
418 dquot->dq_dqb.u.v2_mdqb.dqb_off = offset;
419 lseek(h->qh_fd, offset, SEEK_SET);
420 ret = read(h->qh_fd, ddquot, info->dqi_entry_size);
421 if (ret != info->dqi_entry_size) {
424 die(2, _("Cannot read quota structure for id %u: %s\n"), dquot->dq_id,
427 info->dqi_ops->disk2mem_dqblk(dquot, ddquot);
433 * Scan all dquots in file and call callback on each
435 #define set_bit(bmp, ind) ((bmp)[(ind) >> 3] |= (1 << ((ind) & 7)))
436 #define get_bit(bmp, ind) ((bmp)[(ind) >> 3] & (1 << ((ind) & 7)))
438 static int report_block(struct dquot *dquot, uint blk, char *bitmap,
439 int (*process_dquot) (struct dquot *, char *))
441 struct qtree_mem_dqinfo *info = &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
442 dqbuf_t buf = getdqbuf();
443 struct qt_disk_dqdbheader *dh;
447 set_bit(bitmap, blk);
448 read_blk(dquot->dq_h, blk, buf);
449 dh = (struct qt_disk_dqdbheader *)buf;
450 ddata = buf + sizeof(struct qt_disk_dqdbheader);
451 entries = __le16_to_cpu(dh->dqdh_entries);
452 for (i = 0; i < qtree_dqstr_in_blk(info); i++, ddata += info->dqi_entry_size)
453 if (!qtree_entry_unused(info, ddata)) {
454 info->dqi_ops->disk2mem_dqblk(dquot, ddata);
455 if (process_dquot(dquot, NULL) < 0)
462 static void check_reference(struct quota_handle *h, uint blk)
464 if (blk >= h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks)
465 die(2, _("Illegal reference (%u >= %u) in %s quota file on %s. Quota file is probably corrupted.\nPlease run quotacheck(8) and try again.\n"), blk, h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks, type2name(h->qh_type), h->qh_quotadev);
468 static int report_tree(struct dquot *dquot, uint blk, int depth, char *bitmap,
469 int (*process_dquot) (struct dquot *, char *))
472 dqbuf_t buf = getdqbuf();
473 u_int32_t *ref = (u_int32_t *) buf;
475 read_blk(dquot->dq_h, blk, buf);
476 if (depth == QT_TREEDEPTH - 1) {
477 for (i = 0; i < QT_BLKSIZE >> 2; i++) {
478 blk = __le32_to_cpu(ref[i]);
479 check_reference(dquot->dq_h, blk);
480 if (blk && !get_bit(bitmap, blk))
481 entries += report_block(dquot, blk, bitmap, process_dquot);
485 for (i = 0; i < QT_BLKSIZE >> 2; i++)
486 if ((blk = __le32_to_cpu(ref[i]))) {
487 check_reference(dquot->dq_h, blk);
489 report_tree(dquot, blk, depth + 1, bitmap, process_dquot);
496 static uint find_set_bits(char *bmp, int blocks)
500 for (i = 0; i < blocks; i++)
506 int qtree_scan_dquots(struct quota_handle *h, int (*process_dquot) (struct dquot *, char *))
509 struct v2_mem_dqinfo *v2info = &h->qh_info.u.v2_mdqi;
510 struct qtree_mem_dqinfo *info = &v2info->dqi_qtree;
511 struct dquot *dquot = get_empty_dquot();
514 bitmap = smalloc((info->dqi_blocks + 7) >> 3);
515 memset(bitmap, 0, (info->dqi_blocks + 7) >> 3);
516 v2info->dqi_used_entries = report_tree(dquot, QT_TREEOFF, 0, bitmap, process_dquot);
517 v2info->dqi_data_blocks = find_set_bits(bitmap, info->dqi_blocks);