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