quotaio_v2.c revision 198d20fc74a6124548b96e4c14c38ff8d5a95ae5
1/* 2 * Implementation of new quotafile format 3 * 4 * Jan Kara <jack@suse.cz> - sponsored by SuSE CR 5 */ 6 7#include "config.h" 8#include <sys/types.h> 9#include <errno.h> 10#include <stdio.h> 11#include <stdlib.h> 12#include <string.h> 13#include <unistd.h> 14 15#include "common.h" 16#include "quotaio_v2.h" 17#include "dqblk_v2.h" 18#include "quotaio.h" 19#include "quotaio_tree.h" 20 21typedef char *dqbuf_t; 22 23static int v2_check_file(struct quota_handle *h, int type, int fmt); 24static int v2_init_io(struct quota_handle *h); 25static int v2_new_io(struct quota_handle *h); 26static int v2_write_info(struct quota_handle *h); 27static struct dquot *v2_read_dquot(struct quota_handle *h, qid_t id); 28static int v2_commit_dquot(struct dquot *dquot); 29static int v2_scan_dquots(struct quota_handle *h, 30 int (*process_dquot) (struct dquot *dquot, 31 void *data), 32 void *data); 33static int v2_report(struct quota_handle *h, int verbose); 34 35struct quotafile_ops quotafile_ops_2 = { 36 .check_file = v2_check_file, 37 .init_io = v2_init_io, 38 .new_io = v2_new_io, 39 .write_info = v2_write_info, 40 .read_dquot = v2_read_dquot, 41 .commit_dquot = v2_commit_dquot, 42 .scan_dquots = v2_scan_dquots, 43 .report = v2_report, 44}; 45 46#define getdqbuf() smalloc(V2_DQBLKSIZE) 47#define freedqbuf(buf) free(buf) 48 49/* 50 * Copy dquot from disk to memory 51 */ 52static void v2r1_disk2memdqblk(struct dquot *dquot, void *dp) 53{ 54 struct util_dqblk *m = &dquot->dq_dqb; 55 struct v2r1_disk_dqblk *d = dp, empty; 56 57 dquot->dq_id = ext2fs_le32_to_cpu(d->dqb_id); 58 m->dqb_ihardlimit = ext2fs_le64_to_cpu(d->dqb_ihardlimit); 59 m->dqb_isoftlimit = ext2fs_le64_to_cpu(d->dqb_isoftlimit); 60 m->dqb_bhardlimit = ext2fs_le64_to_cpu(d->dqb_bhardlimit); 61 m->dqb_bsoftlimit = ext2fs_le64_to_cpu(d->dqb_bsoftlimit); 62 m->dqb_curinodes = ext2fs_le64_to_cpu(d->dqb_curinodes); 63 m->dqb_curspace = ext2fs_le64_to_cpu(d->dqb_curspace); 64 m->dqb_itime = ext2fs_le64_to_cpu(d->dqb_itime); 65 m->dqb_btime = ext2fs_le64_to_cpu(d->dqb_btime); 66 67 memset(&empty, 0, sizeof(struct v2r1_disk_dqblk)); 68 empty.dqb_itime = ext2fs_cpu_to_le64(1); 69 if (!memcmp(&empty, dp, sizeof(struct v2r1_disk_dqblk))) 70 m->dqb_itime = 0; 71} 72 73/* 74 * Copy dquot from memory to disk 75 */ 76static void v2r1_mem2diskdqblk(void *dp, struct dquot *dquot) 77{ 78 struct util_dqblk *m = &dquot->dq_dqb; 79 struct v2r1_disk_dqblk *d = dp; 80 81 d->dqb_ihardlimit = ext2fs_cpu_to_le64(m->dqb_ihardlimit); 82 d->dqb_isoftlimit = ext2fs_cpu_to_le64(m->dqb_isoftlimit); 83 d->dqb_bhardlimit = ext2fs_cpu_to_le64(m->dqb_bhardlimit); 84 d->dqb_bsoftlimit = ext2fs_cpu_to_le64(m->dqb_bsoftlimit); 85 d->dqb_curinodes = ext2fs_cpu_to_le64(m->dqb_curinodes); 86 d->dqb_curspace = ext2fs_cpu_to_le64(m->dqb_curspace); 87 d->dqb_itime = ext2fs_cpu_to_le64(m->dqb_itime); 88 d->dqb_btime = ext2fs_cpu_to_le64(m->dqb_btime); 89 d->dqb_id = ext2fs_cpu_to_le32(dquot->dq_id); 90 if (qtree_entry_unused(&dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree, dp)) 91 d->dqb_itime = ext2fs_cpu_to_le64(1); 92} 93 94static int v2r1_is_id(void *dp, struct dquot *dquot) 95{ 96 struct v2r1_disk_dqblk *d = dp; 97 struct qtree_mem_dqinfo *info = 98 &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree; 99 100 if (qtree_entry_unused(info, dp)) 101 return 0; 102 return ext2fs_le32_to_cpu(d->dqb_id) == dquot->dq_id; 103} 104 105static struct qtree_fmt_operations v2r1_fmt_ops = { 106 .mem2disk_dqblk = v2r1_mem2diskdqblk, 107 .disk2mem_dqblk = v2r1_disk2memdqblk, 108 .is_id = v2r1_is_id, 109}; 110 111/* 112 * Copy dqinfo from disk to memory 113 */ 114static inline void v2_disk2memdqinfo(struct util_dqinfo *m, 115 struct v2_disk_dqinfo *d) 116{ 117 m->dqi_bgrace = ext2fs_le32_to_cpu(d->dqi_bgrace); 118 m->dqi_igrace = ext2fs_le32_to_cpu(d->dqi_igrace); 119 m->u.v2_mdqi.dqi_flags = ext2fs_le32_to_cpu(d->dqi_flags) & V2_DQF_MASK; 120 m->u.v2_mdqi.dqi_qtree.dqi_blocks = ext2fs_le32_to_cpu(d->dqi_blocks); 121 m->u.v2_mdqi.dqi_qtree.dqi_free_blk = 122 ext2fs_le32_to_cpu(d->dqi_free_blk); 123 m->u.v2_mdqi.dqi_qtree.dqi_free_entry = 124 ext2fs_le32_to_cpu(d->dqi_free_entry); 125} 126 127/* 128 * Copy dqinfo from memory to disk 129 */ 130static inline void v2_mem2diskdqinfo(struct v2_disk_dqinfo *d, 131 struct util_dqinfo *m) 132{ 133 d->dqi_bgrace = ext2fs_cpu_to_le32(m->dqi_bgrace); 134 d->dqi_igrace = ext2fs_cpu_to_le32(m->dqi_igrace); 135 d->dqi_flags = ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_flags & V2_DQF_MASK); 136 d->dqi_blocks = ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_blocks); 137 d->dqi_free_blk = 138 ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_free_blk); 139 d->dqi_free_entry = 140 ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_free_entry); 141} 142 143/* Convert kernel quotablock format to utility one */ 144static inline void v2_kern2utildqblk(struct util_dqblk *u, 145 struct v2_kern_dqblk *k) 146{ 147 u->dqb_ihardlimit = k->dqb_ihardlimit; 148 u->dqb_isoftlimit = k->dqb_isoftlimit; 149 u->dqb_bhardlimit = k->dqb_bhardlimit; 150 u->dqb_bsoftlimit = k->dqb_bsoftlimit; 151 u->dqb_curinodes = k->dqb_curinodes; 152 u->dqb_curspace = k->dqb_curspace; 153 u->dqb_itime = k->dqb_itime; 154 u->dqb_btime = k->dqb_btime; 155} 156 157/* Convert utility quotablock format to kernel one */ 158static inline void v2_util2kerndqblk(struct v2_kern_dqblk *k, 159 struct util_dqblk *u) 160{ 161 k->dqb_ihardlimit = u->dqb_ihardlimit; 162 k->dqb_isoftlimit = u->dqb_isoftlimit; 163 k->dqb_bhardlimit = u->dqb_bhardlimit; 164 k->dqb_bsoftlimit = u->dqb_bsoftlimit; 165 k->dqb_curinodes = u->dqb_curinodes; 166 k->dqb_curspace = u->dqb_curspace; 167 k->dqb_itime = u->dqb_itime; 168 k->dqb_btime = u->dqb_btime; 169} 170 171static int v2_read_header(struct quota_handle *h, struct v2_disk_dqheader *dqh) 172{ 173 if (h->e2fs_read(&h->qh_qf, 0, dqh, sizeof(struct v2_disk_dqheader)) != 174 sizeof(struct v2_disk_dqheader)) 175 return 0; 176 177 return 1; 178} 179 180/* 181 * Check whether given quota file is in our format 182 */ 183static int v2_check_file(struct quota_handle *h, int type, int fmt) 184{ 185 struct v2_disk_dqheader dqh; 186 int file_magics[] = INITQMAGICS; 187 int known_versions[] = INIT_V2_VERSIONS; 188 int version; 189 190 if (!v2_read_header(h, &dqh)) 191 return 0; 192 if (fmt == QFMT_VFS_V0) 193 version = 0; 194 else if (fmt == QFMT_VFS_V1) 195 version = 1; 196 else 197 return 0; 198 199 if (ext2fs_le32_to_cpu(dqh.dqh_magic) != file_magics[type]) { 200 if (ext2fs_be32_to_cpu(dqh.dqh_magic) == file_magics[type]) 201 log_err("Your quota file is stored in wrong " 202 "endianity.", ""); 203 return 0; 204 } 205 if (ext2fs_le32_to_cpu(dqh.dqh_version) > known_versions[type]) 206 return 0; 207 if (version != ext2fs_le32_to_cpu(dqh.dqh_version)) 208 return 0; 209 return 1; 210} 211 212/* 213 * Open quotafile 214 */ 215static int v2_init_io(struct quota_handle *h) 216{ 217 struct v2_disk_dqinfo ddqinfo; 218 219 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_entry_size = 220 sizeof(struct v2r1_disk_dqblk); 221 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_ops = &v2r1_fmt_ops; 222 223 /* Read information about quotafile */ 224 if (h->e2fs_read(&h->qh_qf, V2_DQINFOOFF, &ddqinfo, 225 sizeof(ddqinfo)) != sizeof(ddqinfo)) 226 return -1; 227 v2_disk2memdqinfo(&h->qh_info, &ddqinfo); 228 return 0; 229} 230 231/* 232 * Initialize new quotafile 233 */ 234static int v2_new_io(struct quota_handle *h) 235{ 236 int file_magics[] = INITQMAGICS; 237 struct v2_disk_dqheader ddqheader; 238 struct v2_disk_dqinfo ddqinfo; 239 int version = 1; 240 241 if (h->qh_fmt != QFMT_VFS_V1) 242 return -1; 243 244 /* Write basic quota header */ 245 ddqheader.dqh_magic = ext2fs_cpu_to_le32(file_magics[h->qh_type]); 246 ddqheader.dqh_version = ext2fs_cpu_to_le32(version); 247 if (h->e2fs_write(&h->qh_qf, 0, &ddqheader, sizeof(ddqheader)) != 248 sizeof(ddqheader)) 249 return -1; 250 251 /* Write information about quotafile */ 252 h->qh_info.dqi_bgrace = MAX_DQ_TIME; 253 h->qh_info.dqi_igrace = MAX_IQ_TIME; 254 h->qh_info.u.v2_mdqi.dqi_flags = 0; 255 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks = QT_TREEOFF + 1; 256 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_blk = 0; 257 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_entry = 0; 258 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_entry_size = 259 sizeof(struct v2r1_disk_dqblk); 260 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_ops = &v2r1_fmt_ops; 261 v2_mem2diskdqinfo(&ddqinfo, &h->qh_info); 262 if (h->e2fs_write(&h->qh_qf, V2_DQINFOOFF, &ddqinfo, 263 sizeof(ddqinfo)) != 264 sizeof(ddqinfo)) 265 return -1; 266 267 return 0; 268} 269 270/* 271 * Write information (grace times to file) 272 */ 273static int v2_write_info(struct quota_handle *h) 274{ 275 struct v2_disk_dqinfo ddqinfo; 276 277 v2_mem2diskdqinfo(&ddqinfo, &h->qh_info); 278 if (h->e2fs_write(&h->qh_qf, V2_DQINFOOFF, &ddqinfo, sizeof(ddqinfo)) != 279 sizeof(ddqinfo)) 280 return -1; 281 282 return 0; 283} 284 285/* 286 * Read dquot from disk 287 */ 288static struct dquot *v2_read_dquot(struct quota_handle *h, qid_t id) 289{ 290 return qtree_read_dquot(h, id); 291} 292 293/* 294 * Commit changes of dquot to disk - it might also mean deleting it when quota 295 * became fake one and user has no blocks. 296 * User can process use 'errno' to detect errstr. 297 */ 298static int v2_commit_dquot(struct dquot *dquot) 299{ 300 struct util_dqblk *b = &dquot->dq_dqb; 301 302 if (!b->dqb_curspace && !b->dqb_curinodes && !b->dqb_bsoftlimit && 303 !b->dqb_isoftlimit && !b->dqb_bhardlimit && !b->dqb_ihardlimit) 304 qtree_delete_dquot(dquot); 305 else 306 qtree_write_dquot(dquot); 307 return 0; 308} 309 310static int v2_scan_dquots(struct quota_handle *h, 311 int (*process_dquot) (struct dquot *, void *), 312 void *data) 313{ 314 return qtree_scan_dquots(h, process_dquot, data); 315} 316 317/* Report information about quotafile. 318 * TODO: Not used right now, but we should be able to use this when we add 319 * support to debugfs to read quota files. 320 */ 321static int v2_report(struct quota_handle *h, int verbose) 322{ 323 log_err("Not Implemented.", ""); 324 return -1; 325} 326