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