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