Whamcloud - gitweb
libext2fs: Add some fail-safe checks to the 32-bit bitmap code
[tools/e2fsprogs.git] / lib / ext2fs / gen_bitmap64.c
1 /*
2  * gen_bitmap64.c --- routines to read, write, and manipulate the new qinode and
3  * block bitmaps.
4  *
5  * Copyright (C) 2007, 2008 Theodore Ts'o.
6  *
7  * %Begin-Header%
8  * This file may be redistributed under the terms of the GNU Public
9  * License.
10  * %End-Header%
11  */
12
13 #include <stdio.h>
14 #include <string.h>
15 #if HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
18 #include <fcntl.h>
19 #include <time.h>
20 #include <errno.h>
21 #if HAVE_SYS_STAT_H
22 #include <sys/stat.h>
23 #endif
24 #if HAVE_SYS_TYPES_H
25 #include <sys/types.h>
26 #endif
27
28 #include "ext2_fs.h"
29 #include "ext2fsP.h"
30 #include "bmap64.h"
31
32 /*
33  * Design of 64-bit bitmaps
34  *
35  * In order maintain ABI compatibility with programs that don't
36  * understand about 64-bit blocks/inodes,
37  * ext2fs_allocate_inode_bitmap() and ext2fs_allocate_block_bitmap()
38  * will create old-style bitmaps unless the application passes the
39  * flag EXT2_FLAG_64BITS to ext2fs_open().  If this flag is
40  * passed, then we know the application has been recompiled, so we can
41  * use the new-style bitmaps.  If it is not passed, we have to return
42  * an error if trying to open a filesystem which needs 64-bit bitmaps.
43  *
44  * The new bitmaps use a new set of structure magic numbers, so that
45  * both the old-style and new-style interfaces can identify which
46  * version of the data structure was used.  Both the old-style and
47  * new-style interfaces will support either type of bitmap, although
48  * of course 64-bit operation will only be possible when both the
49  * new-style interface and the new-style bitmap are used.
50  *
51  * For example, the new bitmap interfaces will check the structure
52  * magic numbers and so will be able to detect old-stype bitmap.  If
53  * they see an old-style bitmap, they will pass it to the gen_bitmap.c
54  * functions for handling.  The same will be true for the old
55  * interfaces as well.
56  *
57  * The new-style interfaces will have several different back-end
58  * implementations, so we can support different encodings that are
59  * appropriate for different applications.  In general the default
60  * should be whatever makes sense, and what the application/library
61  * will use.  However, e2fsck may need specialized implementations for
62  * its own uses.  For example, when doing parent directory pointer
63  * loop detections in pass 3, the bitmap will *always* be sparse, so
64  * e2fsck can request an encoding which is optimized for that.
65  */
66
67 static void warn_bitmap(ext2fs_generic_bitmap bitmap,
68                         int code, __u64 arg)
69 {
70 #ifndef OMIT_COM_ERR
71         if (bitmap->description)
72                 com_err(0, bitmap->base_error_code+code,
73                         "#%llu for %s", arg, bitmap->description);
74         else
75                 com_err(0, bitmap->base_error_code + code, "#%llu", arg);
76 #endif
77 }
78
79
80 errcode_t ext2fs_alloc_generic_bmap(ext2_filsys fs, errcode_t magic,
81                                     int type, __u64 start, __u64 end,
82                                     __u64 real_end,
83                                     const char *descr,
84                                     ext2fs_generic_bitmap *ret)
85 {
86         ext2fs_generic_bitmap   bitmap;
87         struct ext2_bitmap_ops  *ops;
88         errcode_t retval;
89
90         switch (type) {
91         case EXT2FS_BMAP64_BITARRAY:
92                 ops = &ext2fs_blkmap64_bitarray;
93                 break;
94         default:
95                 return EINVAL;
96         }
97
98         retval = ext2fs_get_mem(sizeof(struct ext2fs_struct_generic_bitmap),
99                                 &bitmap);
100         if (retval)
101                 return retval;
102
103         /* XXX factor out, repeated in copy_bmap */
104         bitmap->magic = magic;
105         bitmap->fs = fs;
106         bitmap->start = start;
107         bitmap->end = end;
108         bitmap->real_end = real_end;
109         bitmap->bitmap_ops = ops;
110         switch (magic) {
111         case EXT2_ET_MAGIC_INODE_BITMAP64:
112                 bitmap->base_error_code = EXT2_ET_BAD_INODE_MARK;
113                 break;
114         case EXT2_ET_MAGIC_BLOCK_BITMAP64:
115                 bitmap->base_error_code = EXT2_ET_BAD_BLOCK_MARK;
116                 break;
117         default:
118                 bitmap->base_error_code = EXT2_ET_BAD_GENERIC_MARK;
119         }
120         if (descr) {
121                 retval = ext2fs_get_mem(strlen(descr)+1, &bitmap->description);
122                 if (retval) {
123                         ext2fs_free_mem(&bitmap);
124                         return retval;
125                 }
126                 strcpy(bitmap->description, descr);
127         } else
128                 bitmap->description = 0;
129
130         retval = bitmap->bitmap_ops->new_bmap(fs, bitmap);
131         if (retval) {
132                 ext2fs_free_mem(&bitmap);
133                 ext2fs_free_mem(&bitmap->description);
134                 return retval;
135         }
136
137         *ret = bitmap;
138         return 0;
139 }
140
141 void ext2fs_free_generic_bmap(ext2fs_generic_bitmap bmap)
142 {
143         if (!bmap)
144                 return;
145
146         if (EXT2FS_IS_32_BITMAP(bmap)) {
147                 ext2fs_free_generic_bitmap((ext2fs_generic_bitmap) bmap);
148                 return;
149         }
150
151         if (!EXT2FS_IS_64_BITMAP(bmap))
152                 return;
153
154         bmap->bitmap_ops->free_bmap(bmap);
155
156         if (bmap->description) {
157                 ext2fs_free_mem(&bmap->description);
158                 bmap->description = 0;
159         }
160         bmap->magic = 0;
161 }
162
163 errcode_t ext2fs_copy_generic_bmap(ext2fs_generic_bitmap src,
164                                    ext2fs_generic_bitmap *dest)
165 {
166         char *descr, *new_descr;
167         ext2fs_generic_bitmap   new_bmap;
168         errcode_t retval;
169
170         if (!src)
171                 return EINVAL;
172
173         if (EXT2FS_IS_32_BITMAP(src))
174                 return ext2fs_copy_generic_bitmap((ext2fs_generic_bitmap) src,
175                                                   (ext2fs_generic_bitmap *) dest);
176
177         if (!EXT2FS_IS_64_BITMAP(src))
178                 return EINVAL;
179
180         /* Allocate a new bitmap struct */
181         retval = ext2fs_get_mem(sizeof(struct ext2fs_struct_generic_bitmap),
182                                 &new_bmap);
183         if (retval)
184                 return retval;
185
186         /* Copy all the high-level parts over */
187         new_bmap->magic = src->magic;
188         new_bmap->fs = src->fs;
189         new_bmap->start = src->start;
190         new_bmap->end = src->end;
191         new_bmap->real_end = src->real_end;
192         new_bmap->bitmap_ops = src->bitmap_ops;
193         new_bmap->base_error_code = src->base_error_code;
194
195         descr = src->description;
196         if (descr) {
197                 retval = ext2fs_get_mem(strlen(descr)+1, &new_descr);
198                 if (retval) {
199                         ext2fs_free_mem(&new_bmap);
200                         return retval;
201                 }
202                 strcpy(new_descr, descr);
203                 new_bmap->description = new_descr;
204         }
205
206         retval = src->bitmap_ops->copy_bmap(src, new_bmap);
207         if (retval) {
208                 ext2fs_free_mem(&new_bmap->description);
209                 ext2fs_free_mem(&new_bmap);
210                 return retval;
211         }
212
213         *dest = new_bmap;
214
215         return 0;
216 }
217
218 errcode_t ext2fs_resize_generic_bmap(ext2fs_generic_bitmap bmap,
219                                      __u64 new_end,
220                                      __u64 new_real_end)
221 {
222         if (!bmap)
223                 return EINVAL;
224
225         if (EXT2FS_IS_32_BITMAP(bmap)) {
226                 return ext2fs_resize_generic_bitmap(bmap->magic,
227                                                     new_end, new_real_end,
228                                                     (ext2fs_generic_bitmap) bmap);
229         }
230
231         if (!EXT2FS_IS_64_BITMAP(bmap))
232                 return EINVAL;
233
234         return bmap->bitmap_ops->resize_bmap(bmap, new_end, new_real_end);
235 }
236
237 errcode_t ext2fs_fudge_generic_bmap_end(ext2fs_generic_bitmap bitmap,
238                                         errcode_t neq,
239                                         __u64 end, __u64 *oend)
240 {
241         if (!bitmap)
242                 return EINVAL;
243
244         if (EXT2FS_IS_32_BITMAP(bitmap)) {
245                 ext2_ino_t tmp_oend;
246                 int retval;
247
248                 retval = ext2fs_fudge_generic_bitmap_end((ext2fs_generic_bitmap) bitmap,
249                                                          bitmap->magic, neq,
250                                                          end, &tmp_oend);
251                 if (oend)
252                         *oend = tmp_oend;
253                 return retval;
254         }
255
256         if (!EXT2FS_IS_64_BITMAP(bitmap))
257                 return EINVAL;
258
259         if (end > bitmap->real_end)
260                 return neq;
261         if (oend)
262                 *oend = bitmap->end;
263         bitmap->end = end;
264         return 0;
265 }
266
267 __u64 ext2fs_get_generic_bmap_start(ext2fs_generic_bitmap bitmap)
268 {
269         if (!bitmap)
270                 return EINVAL;
271
272         if (EXT2FS_IS_32_BITMAP(bitmap)) {
273                 return ext2fs_get_generic_bitmap_start((ext2fs_generic_bitmap)
274                                                        bitmap);
275
276         }
277
278         if (!EXT2FS_IS_64_BITMAP(bitmap))
279                 return EINVAL;
280
281         return bitmap->start;
282 }
283
284 __u64 ext2fs_get_generic_bmap_end(ext2fs_generic_bitmap bitmap)
285 {
286         if (!bitmap)
287                 return EINVAL;
288
289         if (EXT2FS_IS_32_BITMAP(bitmap)) {
290                 return ext2fs_get_generic_bitmap_end((ext2fs_generic_bitmap)
291                                                        bitmap);
292
293         }
294
295         if (!EXT2FS_IS_64_BITMAP(bitmap))
296                 return EINVAL;
297
298         return bitmap->end;
299 }
300
301 void ext2fs_clear_generic_bmap(ext2fs_generic_bitmap bitmap)
302 {
303         if (EXT2FS_IS_32_BITMAP(bitmap)) {
304                 ext2fs_clear_generic_bitmap((ext2fs_generic_bitmap) bitmap);
305                 return;
306         }
307
308         bitmap->bitmap_ops->clear_bmap (bitmap);
309 }
310
311 int ext2fs_mark_generic_bmap(ext2fs_generic_bitmap bitmap,
312                              __u64 arg)
313 {
314         if (!bitmap)
315                 return 0;
316
317         if (EXT2FS_IS_32_BITMAP(bitmap)) {
318                 if (arg & ~0xffffffffULL) {
319                         ext2fs_warn_bitmap2((ext2fs_generic_bitmap) bitmap,
320                                             EXT2FS_MARK_ERROR, 0xffffffff);
321                         return 0;
322                 }
323                 return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap)
324                                                   bitmap, arg);
325         }
326
327         if (!EXT2FS_IS_64_BITMAP(bitmap))
328                 return 0;
329
330         if ((arg < bitmap->start) || (arg > bitmap->end)) {
331                 warn_bitmap(bitmap, EXT2FS_MARK_ERROR, arg);
332                 return 0;
333         }
334
335         return bitmap->bitmap_ops->mark_bmap(bitmap, arg);
336 }
337
338 int ext2fs_unmark_generic_bmap(ext2fs_generic_bitmap bitmap,
339                                __u64 arg)
340 {
341         if (!bitmap)
342                 return 0;
343
344         if (EXT2FS_IS_32_BITMAP(bitmap)) {
345                 if (arg & ~0xffffffffULL) {
346                         ext2fs_warn_bitmap2((ext2fs_generic_bitmap) bitmap,
347                                             EXT2FS_UNMARK_ERROR, 0xffffffff);
348                         return 0;
349                 }
350                 return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap)
351                                                   bitmap, arg);
352         }
353
354         if (!EXT2FS_IS_64_BITMAP(bitmap))
355                 return 0;
356
357         if ((arg < bitmap->start) || (arg > bitmap->end)) {
358                 warn_bitmap(bitmap, EXT2FS_UNMARK_ERROR, arg);
359                 return 0;
360         }
361
362         return bitmap->bitmap_ops->unmark_bmap(bitmap, arg);
363 }
364
365 int ext2fs_test_generic_bmap(ext2fs_generic_bitmap bitmap,
366                              __u64 arg)
367 {
368         if (!bitmap)
369                 return 0;
370
371         if (EXT2FS_IS_32_BITMAP(bitmap)) {
372                 if (arg & ~0xffffffffULL) {
373                         ext2fs_warn_bitmap2((ext2fs_generic_bitmap) bitmap,
374                                             EXT2FS_TEST_ERROR, 0xffffffff);
375                         return 0;
376                 }
377                 return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap)
378                                                   bitmap, arg);
379         }
380
381         if (!EXT2FS_IS_64_BITMAP(bitmap))
382                 return 0;
383
384         if ((arg < bitmap->start) || (arg > bitmap->end)) {
385                 warn_bitmap(bitmap, EXT2FS_TEST_ERROR, arg);
386                 return 0;
387         }
388
389         return bitmap->bitmap_ops->test_bmap(bitmap, arg);
390 }
391
392 errcode_t ext2fs_set_generic_bmap_range(ext2fs_generic_bitmap bmap,
393                                         __u64 start, unsigned int num,
394                                         void *in)
395 {
396         if (!bmap)
397                 return EINVAL;
398
399         if (EXT2FS_IS_32_BITMAP(bmap)) {
400                 if ((start+num) & ~0xffffffffULL) {
401                         ext2fs_warn_bitmap2((ext2fs_generic_bitmap) bmap,
402                                             EXT2FS_UNMARK_ERROR, 0xffffffff);
403                         return EINVAL;
404                 }
405                 return ext2fs_set_generic_bitmap_range((ext2fs_generic_bitmap) bmap,
406                                                        bmap->magic, start, num,
407                                                        in);
408         }
409
410         if (!EXT2FS_IS_64_BITMAP(bmap))
411                 return EINVAL;
412
413         return bmap->bitmap_ops->set_bmap_range(bmap, start, num, in);
414 }
415
416 errcode_t ext2fs_get_generic_bmap_range(ext2fs_generic_bitmap bmap,
417                                         __u64 start, unsigned int num,
418                                         void *out)
419 {
420         if (!bmap)
421                 return EINVAL;
422
423         if (EXT2FS_IS_32_BITMAP(bmap)) {
424                 if ((start+num) & ~0xffffffffULL) {
425                         ext2fs_warn_bitmap2((ext2fs_generic_bitmap) bmap,
426                                             EXT2FS_UNMARK_ERROR, 0xffffffff);
427                         return EINVAL;
428                 }
429                 return ext2fs_get_generic_bitmap_range((ext2fs_generic_bitmap) bmap,
430                                                        bmap->magic, start, num,
431                                                        out);
432         }
433
434         if (!EXT2FS_IS_64_BITMAP(bmap))
435                 return EINVAL;
436
437         return bmap->bitmap_ops->get_bmap_range(bmap, start, num, out);
438 }
439
440 errcode_t ext2fs_compare_generic_bmap(errcode_t neq,
441                                       ext2fs_generic_bitmap bm1,
442                                       ext2fs_generic_bitmap bm2)
443 {
444         blk64_t i;
445
446         if (!bm1 || !bm2)
447                 return EINVAL;
448         if (bm1->magic != bm2->magic)
449                 return EINVAL;
450
451         /* Now we know both bitmaps have the same magic */
452         if (EXT2FS_IS_32_BITMAP(bm1))
453                 return ext2fs_compare_generic_bitmap(bm1->magic, neq,
454                                              (ext2fs_generic_bitmap) bm1,
455                                              (ext2fs_generic_bitmap) bm2);
456
457         if (!EXT2FS_IS_64_BITMAP(bm1))
458                 return EINVAL;
459
460         if ((bm1->start != bm2->start) ||
461             (bm1->end != bm2->end))
462                 return neq;
463
464         for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++)
465                 if (ext2fs_test_generic_bmap(bm1, i) !=
466                     ext2fs_test_generic_bmap(bm2, i))
467                         return neq;
468
469         return 0;
470 }
471
472 void ext2fs_set_generic_bmap_padding(ext2fs_generic_bitmap bmap)
473 {
474         __u64   start, num;
475
476         if (EXT2FS_IS_32_BITMAP(bmap)) {
477                 ext2fs_set_generic_bitmap_padding((ext2fs_generic_bitmap) bmap);
478                 return;
479         }
480
481         start = bmap->end + 1;
482         num = bmap->real_end - bmap->end;
483         bmap->bitmap_ops->mark_bmap_extent(bmap, start, num);
484         /* XXX ought to warn on error */
485 }
486
487 int ext2fs_test_block_bitmap_range2(ext2fs_block_bitmap bmap,
488                                     blk64_t block, unsigned int num)
489 {
490         if (!bmap)
491                 return EINVAL;
492
493         if (num == 1)
494                 return !ext2fs_test_generic_bmap((ext2fs_generic_bitmap)
495                                                  bmap, block);
496
497         if (EXT2FS_IS_32_BITMAP(bmap)) {
498                 if ((block+num) & ~0xffffffffULL) {
499                         ext2fs_warn_bitmap2((ext2fs_generic_bitmap) bmap,
500                                             EXT2FS_UNMARK_ERROR, 0xffffffff);
501                         return EINVAL;
502                 }
503                 return ext2fs_test_block_bitmap_range(
504                         (ext2fs_generic_bitmap) bmap, block, num);
505         }
506
507         if (!EXT2FS_IS_64_BITMAP(bmap))
508                 return EINVAL;
509
510         return bmap->bitmap_ops->test_clear_bmap_extent(bmap, block, num);
511 }
512
513 void ext2fs_mark_block_bitmap_range2(ext2fs_block_bitmap bmap,
514                                      blk64_t block, unsigned int num)
515 {
516         if (!bmap)
517                 return;
518
519         if (EXT2FS_IS_32_BITMAP(bmap)) {
520                 if ((block+num) & ~0xffffffffULL) {
521                         ext2fs_warn_bitmap2((ext2fs_generic_bitmap) bmap,
522                                             EXT2FS_UNMARK_ERROR, 0xffffffff);
523                         return;
524                 }
525                 ext2fs_mark_block_bitmap_range((ext2fs_generic_bitmap) bmap,
526                                                block, num);
527         }
528
529         if (!EXT2FS_IS_64_BITMAP(bmap))
530                 return;
531
532         if ((block < bmap->start) || (block+num-1 > bmap->end)) {
533                 ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block,
534                                    bmap->description);
535                 return;
536         }
537
538         bmap->bitmap_ops->mark_bmap_extent(bmap, block, num);
539 }
540
541 void ext2fs_unmark_block_bitmap_range2(ext2fs_block_bitmap bmap,
542                                        blk64_t block, unsigned int num)
543 {
544         if (!bmap)
545                 return;
546
547         if (EXT2FS_IS_32_BITMAP(bmap)) {
548                 if ((block+num) & ~0xffffffffULL) {
549                         ext2fs_warn_bitmap2((ext2fs_generic_bitmap) bmap,
550                                             EXT2FS_UNMARK_ERROR, 0xffffffff);
551                         return;
552                 }
553                 ext2fs_unmark_block_bitmap_range((ext2fs_generic_bitmap) bmap,
554                                                  block, num);
555         }
556
557         if (!EXT2FS_IS_64_BITMAP(bmap))
558                 return;
559
560         if ((block < bmap->start) || (block+num-1 > bmap->end)) {
561                 ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, block,
562                                    bmap->description);
563                 return;
564         }
565
566         bmap->bitmap_ops->unmark_bmap_extent(bmap, block, num);
567 }
568
569 int ext2fs_warn_bitmap32(ext2fs_generic_bitmap bitmap, const char *func)
570 {
571 #ifndef OMIT_COM_ERR
572         if (bitmap && bitmap->description)
573                 com_err(0, EXT2_ET_MAGIC_GENERIC_BITMAP,
574                         "called %s with 64-bit bitmap for %s", func,
575                         bitmap->description);
576         else
577                 com_err(0, EXT2_ET_MAGIC_GENERIC_BITMAP,
578                         "called %s with 64-bit bitmap", func);
579 #endif
580 }