/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * quota.c * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * Corruptions for quota system file. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. */ #include "main.h" extern char *progname; static int g_actref; /* This file will corrupt quota system file. * * Quota Error: QMAGIC_INVALID, QTREE_BLK_INVALID, DQBLK_INVALID * DUP_DQBLK_INVALID, DUP_DQBLK_VALID * */ static char *type2name(int type) { if (type == USRQUOTA) return "user"; return "group"; } static errcode_t o2fswreck_read_blk(ocfs2_filesys *fs, int type, char *buf, uint32_t blk) { uint32_t got; errcode_t ret; ret = ocfs2_file_read(fs->qinfo[type].qi_inode, buf, fs->fs_blocksize, blk * fs->fs_blocksize, &got); if (ret) return ret; if (got != fs->fs_blocksize) return OCFS2_ET_SHORT_READ; return 0; } static errcode_t o2fswreck_write_blk(ocfs2_filesys *fs, int type, char *buf, uint32_t blk) { errcode_t err; uint32_t written; err = ocfs2_file_write(fs->qinfo[type].qi_inode, buf, fs->fs_blocksize, blk * fs->fs_blocksize, &written); if (err) return err; if (written != fs->fs_blocksize) return OCFS2_ET_SHORT_WRITE; return 0; } static errcode_t o2fswreck_get_data_blk(ocfs2_filesys *fs, int type, uint32_t blk, int depth, char *buf) { errcode_t ret; int epb = (fs->fs_blocksize - OCFS2_QBLK_RESERVED_SPACE) >> 2; int tree_depth = ocfs2_qtree_depth(fs->fs_blocksize); uint32_t *refs = (uint32_t *)buf, actref; int i; ret = o2fswreck_read_blk(fs, type, buf, blk); if (ret) FSWRK_COM_FATAL(progname, ret); for (i = 0; i < epb; i++) { actref = le32_to_cpu(refs[i]); if (!actref) continue; if (depth + 1 < tree_depth) { ret = o2fswreck_get_data_blk(fs, type, actref, depth + 1, buf + fs->fs_blocksize); } else { ret = o2fswreck_read_blk(fs, type, buf + fs->fs_blocksize, actref); if (ret) FSWRK_COM_FATAL(progname, ret); g_actref = actref; return 0; } if (ret) goto out; } out: return ret; } void mess_up_quota(ocfs2_filesys *fs, enum fsck_type type, uint16_t slotnum) { errcode_t ret; int qtype; char *buf; int tree_depth = ocfs2_qtree_depth(fs->fs_blocksize); struct ocfs2_disk_dqheader *header; struct ocfs2_global_disk_dqblk *ddquot; struct qt_disk_dqdbheader *dh; if ((!OCFS2_HAS_RO_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super), OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) || (!OCFS2_HAS_RO_COMPAT_FEATURE(OCFS2_RAW_SB(fs->fs_super), OCFS2_FEATURE_RO_COMPAT_GRPQUOTA))) FSWRK_FATAL("Should specify a volume with both usrquota and " "grpquota enabled to do this corruption.\n"); ret = ocfs2_init_fs_quota_info(fs, USRQUOTA); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_init_fs_quota_info(fs, GRPQUOTA); if (ret) FSWRK_COM_FATAL(progname, ret); ret = ocfs2_malloc_blocks(fs->fs_io, tree_depth + 1, &buf); if (ret) FSWRK_COM_FATAL(progname, ret); switch (type) { case QMAGIC_INVALID: qtype = USRQUOTA; ret = o2fswreck_read_blk(fs, qtype, buf, 0); if (ret) FSWRK_COM_FATAL(progname, ret); header = (struct ocfs2_disk_dqheader *)buf; ocfs2_swap_quota_header(header); header->dqh_magic = ~header->dqh_magic; ocfs2_swap_quota_header(header); ret = o2fswreck_write_blk(fs, qtype, buf, 0); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "QMAGIC_INVALID: " "Corrupt global %s quota file's magic number " "in its header.\n", type2name(qtype)); break; case QTREE_BLK_INVALID: qtype = GRPQUOTA; ret = o2fswreck_read_blk(fs, qtype, buf, QT_TREEOFF); if (ret) FSWRK_COM_FATAL(progname, ret); struct ocfs2_disk_dqtrailer *dqt = ocfs2_block_dqtrailer(fs->fs_blocksize, buf); dqt->dq_check.bc_crc32e = ~dqt->dq_check.bc_crc32e; dqt->dq_check.bc_ecc = ~dqt->dq_check.bc_ecc; ret = o2fswreck_write_blk(fs, qtype, buf, QT_TREEOFF); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "QTREE_BLK_INVALID: " "Corrupt global %s quota tree block.\n", type2name(qtype)); break; case DQBLK_INVALID: qtype = USRQUOTA; ret = o2fswreck_get_data_blk(fs, qtype, QT_TREEOFF, 0, buf); ddquot = (struct ocfs2_global_disk_dqblk *)(buf + fs->fs_blocksize * tree_depth + sizeof(struct qt_disk_dqdbheader)); ocfs2_swap_quota_global_dqblk(ddquot); ddquot->dqb_id = 0xFFFFFFF6; ddquot->dqb_isoftlimit += 1; ddquot->dqb_ihardlimit += 2; ddquot->dqb_bsoftlimit += 3; ddquot->dqb_bhardlimit += 4; ocfs2_swap_quota_global_dqblk(ddquot); dh = (struct qt_disk_dqdbheader *)(buf + fs->fs_blocksize * tree_depth); ocfs2_swap_quota_leaf_block_header(dh); dh->dqdh_next_free = 0xFFFFFFFF; dh->dqdh_prev_free = 0xFFFFFFFF; dh->dqdh_entries = 0xFFFF; ocfs2_swap_quota_leaf_block_header(dh); ret = o2fswreck_write_blk(fs, qtype, buf + fs->fs_blocksize * tree_depth, g_actref); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "DQBLK_INVALID: " "Corrupt global %s quota data block.\n", type2name(qtype)); break; case DUP_DQBLK_INVALID: qtype = GRPQUOTA; ret = o2fswreck_get_data_blk(fs, qtype, QT_TREEOFF, 0, buf); ddquot = (struct ocfs2_global_disk_dqblk *)(buf + fs->fs_blocksize * tree_depth + sizeof(struct qt_disk_dqdbheader)); ddquot[1].dqb_id = ddquot[0].dqb_id; ddquot[1].dqb_isoftlimit += 1; ddquot[1].dqb_ihardlimit += 2; ddquot[1].dqb_bsoftlimit += 3; ddquot[1].dqb_bhardlimit += 4; dh = (struct qt_disk_dqdbheader *)(buf + fs->fs_blocksize * tree_depth); ocfs2_swap_quota_leaf_block_header(dh); dh->dqdh_next_free = 0xFFFFFFFF; dh->dqdh_prev_free = 0xFFFFFFFF; dh->dqdh_entries = 0xFFFF; ocfs2_swap_quota_leaf_block_header(dh); ret = o2fswreck_write_blk(fs, qtype, buf + fs->fs_blocksize * tree_depth, g_actref); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "DUP_DQBLK_INVALID: " "Duplicate %s quota data block with a invalid entry.\n", type2name(qtype)); break; case DUP_DQBLK_VALID: qtype = GRPQUOTA; ret = o2fswreck_get_data_blk(fs, qtype, QT_TREEOFF, 0, buf); ddquot = (struct ocfs2_global_disk_dqblk *)(buf + fs->fs_blocksize * tree_depth + sizeof(struct qt_disk_dqdbheader)); ddquot[1].dqb_id = ddquot[0].dqb_id; ddquot[1].dqb_isoftlimit = ddquot[0].dqb_isoftlimit; ddquot[1].dqb_ihardlimit = ddquot[0].dqb_isoftlimit; ddquot[1].dqb_bsoftlimit = ddquot[0].dqb_isoftlimit; ddquot[1].dqb_bhardlimit = ddquot[0].dqb_isoftlimit; ret = o2fswreck_write_blk(fs, qtype, buf + fs->fs_blocksize * tree_depth, g_actref); if (ret) FSWRK_COM_FATAL(progname, ret); fprintf(stdout, "DUP_DQBLK_VALID: " "Duplicate %s quota data block with a valid entry.\n", type2name(qtype)); break; default: FSWRK_FATAL("Invalid type[%d]\n", type); } if (buf) ocfs2_free(&buf); }