1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
|
/*
*
* Checking routines for new VFS quota format
*
*/
#include "config.h"
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <endian.h>
#include "pot.h"
#include "common.h"
#include "quota.h"
#include "quotaio.h"
#include "quotaio_v2.h"
#include "quotacheck.h"
#include "quota_tree.h"
#define getdqbuf() smalloc(QT_BLKSIZE)
#define freedqbuf(buf) free(buf)
#define SET_BLK(blk) (blkbmp[(blk) >> 3] |= 1 << ((blk) & 7))
#define GET_BLK(blk) (blkbmp[(blk) >> 3] & (1 << ((blk) & 7)))
typedef char *dqbuf_t;
static const int magics[MAXQUOTAS] = INITQMAGICS; /* Magics we should look for */
static const int known_versions[MAXQUOTAS] = INIT_V2_VERSIONS; /* Versions we accept */
static char *blkbmp; /* Bitmap of checked blocks */
static int detected_versions[MAXQUOTAS];
static int check_blkref(uint blk, uint blocks)
{
if (blk >= blocks)
return -1;
if (blk && blk < QT_TREEOFF)
return -1;
return 0;
}
/* Load and check basic info about quotas */
static int check_info(char *filename, int fd, int type)
{
struct v2_disk_dqinfo dinfo;
uint blocks, dflags, freeblk, freeent;
off_t filesize;
int err;
debug(FL_DEBUG, _("Checking quotafile info...\n"));
lseek(fd, V2_DQINFOOFF, SEEK_SET);
err = read(fd, &dinfo, sizeof(struct v2_disk_dqinfo));
if (err < 0) {
errstr(_("Cannot read info from quota file %s: %s\n"),
filename, strerror(errno));
return -1;
}
if (err != sizeof(struct v2_disk_dqinfo)) {
errstr(_("WARNING - Quota file %s was probably truncated. Cannot save quota settings...\n"),
filename);
return -1;
}
blocks = le32toh(dinfo.dqi_blocks);
freeblk = le32toh(dinfo.dqi_free_blk);
freeent = le32toh(dinfo.dqi_free_entry);
dflags = le32toh(dinfo.dqi_flags);
filesize = lseek(fd, 0, SEEK_END);
if (check_blkref(freeblk, blocks) < 0 || dflags & ~V2_DQF_MASK ||
check_blkref(freeent, blocks) < 0 || (filesize + QT_BLKSIZE - 1) >> QT_BLKSIZE_BITS != blocks) {
errstr(_("WARNING - Quota file info was corrupted.\n"));
debug(FL_DEBUG, _("Size of file: %lu\nBlocks: %u Free block: %u Block with free entry: %u Flags: %x\n"),
(unsigned long)filesize, blocks, freeblk, freeent, dflags);
old_info[type].dqi_bgrace = MAX_DQ_TIME;
old_info[type].dqi_igrace = MAX_IQ_TIME;
old_info[type].u.v2_mdqi.dqi_qtree.dqi_blocks =
(filesize + QT_BLKSIZE - 1) >> QT_BLKSIZE_BITS;
old_info[type].u.v2_mdqi.dqi_flags = 0;
printf(_("Setting grace times and other flags to default values.\nAssuming number of blocks is %u.\n"),
old_info[type].u.v2_mdqi.dqi_qtree.dqi_blocks);
}
else {
old_info[type].dqi_bgrace = le32toh(dinfo.dqi_bgrace);
old_info[type].dqi_igrace = le32toh(dinfo.dqi_igrace);
old_info[type].u.v2_mdqi.dqi_qtree.dqi_blocks = blocks;
old_info[type].u.v2_mdqi.dqi_flags = dflags;
}
if (detected_versions[type] == 0)
old_info[type].u.v2_mdqi.dqi_qtree.dqi_entry_size = sizeof(struct v2r0_disk_dqblk);
else if (detected_versions[type] == 1)
old_info[type].u.v2_mdqi.dqi_qtree.dqi_entry_size = sizeof(struct v2r1_disk_dqblk);
/* Won't be needed */
old_info[type].u.v2_mdqi.dqi_qtree.dqi_free_blk = 0;
old_info[type].u.v2_mdqi.dqi_qtree.dqi_free_entry = 0;
debug(FL_DEBUG, _("File info done.\n"));
return 0;
}
/* Print errstr message */
static void blk_corrupted(int *corrupted, uint * lblk, uint blk, char *fmtstr, ...)
{
va_list args;
if (!*corrupted) {
if (!(flags & (FL_VERBOSE | FL_DEBUG)))
errstr(_("Corrupted blocks: "));
}
if (flags & (FL_VERBOSE | FL_DEBUG)) {
va_start(args, fmtstr);
errstr(_("Block %u: "), blk);
vfprintf(stderr, fmtstr, args);
fputc('\n', stderr);
va_end(args);
}
else if (*lblk != blk) {
if (!*corrupted)
fprintf(stderr, "%u", blk);
else
fprintf(stderr, ", %u", blk);
}
*corrupted = 1;
*lblk = blk;
fflush(stderr);
}
/* Convert dist quota format to utility one - copy just needed fields */
static void v2r0_disk2utildqblk(struct util_dqblk *u, struct v2r0_disk_dqblk *d)
{
u->dqb_ihardlimit = le32toh(d->dqb_ihardlimit);
u->dqb_isoftlimit = le32toh(d->dqb_isoftlimit);
u->dqb_bhardlimit = le32toh(d->dqb_bhardlimit);
u->dqb_bsoftlimit = le32toh(d->dqb_bsoftlimit);
u->dqb_itime = le64toh(d->dqb_itime);
u->dqb_btime = le64toh(d->dqb_btime);
}
/* Convert dist quota format to utility one - copy just needed fields */
static void v2r1_disk2utildqblk(struct util_dqblk *u, struct v2r1_disk_dqblk *d)
{
u->dqb_ihardlimit = le64toh(d->dqb_ihardlimit);
u->dqb_isoftlimit = le64toh(d->dqb_isoftlimit);
u->dqb_bhardlimit = le64toh(d->dqb_bhardlimit);
u->dqb_bsoftlimit = le64toh(d->dqb_bsoftlimit);
u->dqb_itime = le64toh(d->dqb_itime);
u->dqb_btime = le64toh(d->dqb_btime);
}
/* Put one entry info memory */
static int buffer_entry(dqbuf_t buf, uint blk, int *corrupted, uint * lblk, int cnt, int type)
{
struct util_dqblk *fdq, mdq;
qid_t id;
struct dquot *cd;
struct qtree_mem_dqinfo *info = &old_info[type].u.v2_mdqi.dqi_qtree;
char *ddq = (char *)buf + sizeof(struct qt_disk_dqdbheader) + cnt * info->dqi_entry_size;
if (detected_versions[type] == 0) {
v2r0_disk2utildqblk(&mdq, (struct v2r0_disk_dqblk *)ddq);
id = le32toh(((struct v2r0_disk_dqblk *)ddq)->dqb_id);
} else {
v2r1_disk2utildqblk(&mdq, (struct v2r1_disk_dqblk *)ddq);
id = le32toh(((struct v2r1_disk_dqblk *)ddq)->dqb_id);
}
cd = lookup_dquot(id, type);
if (cd != NODQUOT) {
fdq = &cd->dq_dqb;
if (mdq.dqb_bhardlimit != fdq->dqb_bhardlimit
|| mdq.dqb_bsoftlimit != fdq->dqb_bsoftlimit
|| mdq.dqb_ihardlimit != fdq->dqb_ihardlimit
|| mdq.dqb_isoftlimit != fdq->dqb_isoftlimit) {
blk_corrupted(corrupted, lblk, blk, _("Duplicated entries."));
if (flags & FL_GUESSDQ) {
if (!(flags & (FL_DEBUG | FL_VERBOSE)))
fputc('\n', stderr);
errstr(_("Found more structures for ID %u. Using values: BHARD: %lld BSOFT: %lld IHARD: %lld ISOFT: %lld\n"),
(uint) id, (long long)fdq->dqb_bhardlimit, (long long)fdq->dqb_bsoftlimit,
(long long)fdq->dqb_ihardlimit, (long long)fdq->dqb_isoftlimit);
return 0;
}
else if (flags & FL_INTERACTIVE) {
if (!(flags & (FL_DEBUG | FL_VERBOSE)))
fputc('\n', stderr);
errstr(_("Found more structures for ID %u. Values: BHARD: %lld/%lld BSOFT: %lld/%lld IHARD: %lld/%lld ISOFT: %lld/%lld\n"),
(uint) id, (long long)fdq->dqb_bhardlimit, (long long)mdq.dqb_bhardlimit,
(long long)fdq->dqb_bsoftlimit, (long long)mdq.dqb_bsoftlimit,
(long long)fdq->dqb_ihardlimit, (long long)mdq.dqb_ihardlimit,
(long long)fdq->dqb_isoftlimit, (long long)mdq.dqb_isoftlimit);
if (ask_yn(_("Should I use new values?"), 0)) {
fdq->dqb_bhardlimit = mdq.dqb_bhardlimit;
fdq->dqb_bsoftlimit = mdq.dqb_bsoftlimit;
fdq->dqb_ihardlimit = mdq.dqb_ihardlimit;
fdq->dqb_isoftlimit = mdq.dqb_isoftlimit;
fdq->dqb_btime = mdq.dqb_btime;
fdq->dqb_itime = mdq.dqb_itime;
}
}
else {
errstr(_("ID %u has more structures. User intervention needed (use -i for interactive mode or -n for automatic answer).\n"),
(uint) id);
return -1;
}
}
else if (mdq.dqb_itime != fdq->dqb_itime || mdq.dqb_btime != fdq->dqb_btime) {
if (fdq->dqb_btime < mdq.dqb_btime)
fdq->dqb_btime = mdq.dqb_btime;
if (fdq->dqb_itime < mdq.dqb_itime)
fdq->dqb_itime = mdq.dqb_itime;
}
}
else {
cd = add_dquot(id, type);
fdq = &cd->dq_dqb;
fdq->dqb_bhardlimit = mdq.dqb_bhardlimit;
fdq->dqb_bsoftlimit = mdq.dqb_bsoftlimit;
fdq->dqb_ihardlimit = mdq.dqb_ihardlimit;
fdq->dqb_isoftlimit = mdq.dqb_isoftlimit;
/* Add grace times only if there are limits... */
if (mdq.dqb_bsoftlimit)
fdq->dqb_btime = mdq.dqb_btime;
if (mdq.dqb_isoftlimit)
fdq->dqb_itime = mdq.dqb_itime;
}
return 0;
}
static void check_read_blk(int fd, uint blk, dqbuf_t buf)
{
ssize_t rd;
lseek(fd, blk << QT_BLKSIZE_BITS, SEEK_SET);
rd = read(fd, buf, QT_BLKSIZE);
if (rd < 0)
die(2, _("Cannot read block %u: %s\n"), blk, strerror(errno));
if (rd != QT_BLKSIZE) {
debug(FL_VERBOSE | FL_DEBUG, _("Block %u is truncated.\n"), blk);
memset(buf + rd, 0, QT_BLKSIZE - rd);
}
}
static int check_tree_ref(uint blk, uint ref, uint blocks, int check_use, int * corrupted,
uint * lblk)
{
if (check_blkref(ref, blocks) < 0) {
blk_corrupted(corrupted, lblk, blk, _("Reference to illegal block %u"), ref);
return -1;
}
if (!ref)
return 0;
if (!check_use || !GET_BLK(ref))
return 0;
blk_corrupted(corrupted, lblk, blk, _("Block %u in tree referenced twice"), ref);
return -1;
}
/* Check block with structures */
static int check_data_blk(int fd, uint blk, int type, uint blocks, int * corrupted, uint * lblk)
{
dqbuf_t buf = getdqbuf();
struct qt_disk_dqdbheader *head = (struct qt_disk_dqdbheader *)buf;
int i;
char *dd = (char *)(head + 1);
struct qtree_mem_dqinfo *info = &old_info[type].u.v2_mdqi.dqi_qtree;
SET_BLK(blk);
check_read_blk(fd, blk, buf);
if (check_blkref(le32toh(head->dqdh_next_free), blocks) < 0)
blk_corrupted(corrupted, lblk, blk, _("Illegal free block reference to block %u"),
le32toh(head->dqdh_next_free));
if (le16toh(head->dqdh_entries) > qtree_dqstr_in_blk(info))
blk_corrupted(corrupted, lblk, blk, _("Corrupted number of used entries (%u)"),
(uint) le16toh(head->dqdh_entries));
for (i = 0; i < qtree_dqstr_in_blk(info); i++)
if (!qtree_entry_unused(info, dd + i * info->dqi_entry_size))
if (buffer_entry(buf, blk, corrupted, lblk, i, type) < 0) {
freedqbuf(buf);
return -1;
}
freedqbuf(buf);
return 0;
}
/* Check one tree block */
static int check_tree_blk(int fd, uint blk, int depth, int type, uint blocks, int * corrupted,
uint * lblk)
{
dqbuf_t buf = getdqbuf();
u_int32_t *r = (u_int32_t *) buf;
int i;
SET_BLK(blk);
check_read_blk(fd, blk, buf);
for (i = 0; i < QT_BLKSIZE >> 2; i++)
if (depth < QT_TREEDEPTH - 1) {
if (check_tree_ref(blk, le32toh(r[i]), blocks, 1, corrupted, lblk) >= 0 &&
le32toh(r[i])) /* Isn't block OK? */
if (check_tree_blk(fd, le32toh(r[i]), depth + 1, type, blocks, corrupted, lblk) < 0) {
freedqbuf(buf);
return -1;
}
}
else if (check_tree_ref(blk, le32toh(r[i]), blocks, 0, corrupted, lblk) >= 0 && le32toh(r[i]))
if (!GET_BLK(le32toh(r[i])) && check_data_blk(fd, le32toh(r[i]), type, blocks, corrupted, lblk) < 0) {
freedqbuf(buf);
return -1;
}
freedqbuf(buf);
return 0;
}
int v2_detect_version(char *filename, int fd, int type)
{
struct v2_disk_dqheader head;
int err;
int ver;
lseek(fd, 0, SEEK_SET);
err = read(fd, &head, sizeof(head));
if (err < 0 || err != sizeof(head))
return -1;
if (le32toh(head.dqh_magic) != magics[type] ||
le32toh(head.dqh_version) > known_versions[type]) {
errstr(_("Quota file %s has corrupted headers. You have to specify quota format on command line.\n"),
filename);
return -1;
}
ver = le32toh(head.dqh_version);
if (ver == 0)
return QF_VFSV0;
return QF_VFSV1;
}
/* Check basic header */
static int check_header(char *filename, int fd, int type, int version)
{
int err;
struct v2_disk_dqheader head;
debug(FL_DEBUG, _("Checking quotafile headers...\n"));
lseek(fd, 0, SEEK_SET);
err = read(fd, &head, sizeof(head));
if (err < 0)
die(1, _("Cannot read header from quotafile %s: %s\n"), filename, strerror(errno));
if (err != sizeof(head)) {
errstr(_("WARNING - Quotafile %s was probably truncated. Cannot save quota settings...\n"),
filename);
return -1;
}
if (le32toh(head.dqh_magic) != magics[type] ||
le32toh(head.dqh_version) > known_versions[type]) {
errstr(_("WARNING - Quota file %s has corrupted headers\n"),
filename);
if (!(flags & FL_INTERACTIVE) ||
!ask_yn(_("Continue checking assuming format from command line?"), 0))
return -1;
}
if (le32toh(head.dqh_version) != version) {
errstr(_("Quota file format version %d does not match the one "
"specified on command line (%d). Quota file header "
"may be corrupted.\n"),
le32toh(head.dqh_version), version);
if (!(flags & FL_INTERACTIVE) ||
!ask_yn(_("Continue checking assuming version from command line?"), 1))
return -1;
detected_versions[type] = version;
} else
detected_versions[type] = le32toh(head.dqh_version);
debug(FL_DEBUG, _("Headers checked.\n"));
return 0;
}
/* Load data from file to memory */
int v2_buffer_file(char *filename, int fd, int type, int fmt)
{
uint blocks, lastblk = 0;
int corrupted = 0, ret = 0;
int version;
if (fmt == QF_VFSV0)
version = 0;
else if (fmt == QF_VFSV1)
version = 1;
else
die(3, _("Do not know how to buffer format %d\n"), fmt);
old_info[type].dqi_bgrace = MAX_DQ_TIME;
old_info[type].dqi_igrace = MAX_IQ_TIME;
if (flags & FL_NEWFILE)
return 0;
if (check_header(filename, fd, type, version) < 0)
return -1;
if (check_info(filename, fd, type) < 0)
return -1;
debug(FL_DEBUG, _("Headers of file %s checked. Going to load data...\n"),
filename);
blocks = old_info[type].u.v2_mdqi.dqi_qtree.dqi_blocks;
blkbmp = xmalloc((blocks + 7) >> 3);
memset(blkbmp, 0, (blocks + 7) >> 3);
if (check_tree_ref(0, QT_TREEOFF, blocks, 1, &corrupted, &lastblk) >= 0)
ret = check_tree_blk(fd, QT_TREEOFF, 0, type, blocks, &corrupted, &lastblk);
else
errstr(_("Cannot gather quota data. Tree root node corrupted.\n"));
#ifdef DEBUG_MALLOC
free_mem += (blocks + 7) >> 3;
#endif
free(blkbmp);
if (corrupted) {
if (!(flags & (FL_VERBOSE | FL_DEBUG)))
fputc('\n', stderr);
errstr(_("WARNING - Some data might be changed due to corruption.\n"));
}
else
debug(FL_DEBUG, _("Not found any corrupted blocks. Congratulations.\n"));
return ret;
}
/* Merge quotafile info from old and new file */
void v2_merge_info(struct util_dqinfo *new, struct util_dqinfo *old)
{
new->u.v2_mdqi.dqi_flags = old->u.v2_mdqi.dqi_flags;
}
|