summaryrefslogtreecommitdiff
path: root/fs/reiserfs/item_ops.c
blob: e3c558d1b78c02c711ae9868fe409d349127103f (plain)
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
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
/*
 * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README
 */

#include <linux/time.h>
#include "reiserfs.h"

/*
 * this contains item handlers for old item types: sd, direct,
 * indirect, directory
 */

/*
 * and where are the comments? how about saying where we can find an
 * explanation of each item handler method? -Hans
 */

/* stat data functions */
static int sd_bytes_number(struct item_head *ih, int block_size)
{
	return 0;
}

static void sd_decrement_key(struct cpu_key *key)
{
	key->on_disk_key.k_objectid--;
	set_cpu_key_k_type(key, TYPE_ANY);
	set_cpu_key_k_offset(key, (loff_t)(~0ULL >> 1));
}

static int sd_is_left_mergeable(struct reiserfs_key *key, unsigned long bsize)
{
	return 0;
}

static char *print_time(time_t t)
{
	static char timebuf[256];

	sprintf(timebuf, "%ld", t);
	return timebuf;
}

static void sd_print_item(struct item_head *ih, char *item)
{
	printk("\tmode | size | nlinks | first direct | mtime\n");
	if (stat_data_v1(ih)) {
		struct stat_data_v1 *sd = (struct stat_data_v1 *)item;

		printk("\t0%-6o | %6u | %2u | %d | %s\n", sd_v1_mode(sd),
		       sd_v1_size(sd), sd_v1_nlink(sd),
		       sd_v1_first_direct_byte(sd),
		       print_time(sd_v1_mtime(sd)));
	} else {
		struct stat_data *sd = (struct stat_data *)item;

		printk("\t0%-6o | %6llu | %2u | %d | %s\n", sd_v2_mode(sd),
		       (unsigned long long)sd_v2_size(sd), sd_v2_nlink(sd),
		       sd_v2_rdev(sd), print_time(sd_v2_mtime(sd)));
	}
}

static void sd_check_item(struct item_head *ih, char *item)
{
	/* unused */
}

static int sd_create_vi(struct virtual_node *vn,
			struct virtual_item *vi,
			int is_affected, int insert_size)
{
	vi->vi_index = TYPE_STAT_DATA;
	return 0;
}

static int sd_check_left(struct virtual_item *vi, int free,
			 int start_skip, int end_skip)
{
	BUG_ON(start_skip || end_skip);
	return -1;
}

static int sd_check_right(struct virtual_item *vi, int free)
{
	return -1;
}

static int sd_part_size(struct virtual_item *vi, int first, int count)
{
	BUG_ON(count);
	return 0;
}

static int sd_unit_num(struct virtual_item *vi)
{
	return vi->vi_item_len - IH_SIZE;
}

static void sd_print_vi(struct virtual_item *vi)
{
	reiserfs_warning(NULL, "reiserfs-16100",
			 "STATDATA, index %d, type 0x%x, %h",
			 vi->vi_index, vi->vi_type, vi->vi_ih);
}

static struct item_operations stat_data_ops = {
	.bytes_number = sd_bytes_number,
	.decrement_key = sd_decrement_key,
	.is_left_mergeable = sd_is_left_mergeable,
	.print_item = sd_print_item,
	.check_item = sd_check_item,

	.create_vi = sd_create_vi,
	.check_left = sd_check_left,
	.check_right = sd_check_right,
	.part_size = sd_part_size,
	.unit_num = sd_unit_num,
	.print_vi = sd_print_vi
};

/* direct item functions */
static int direct_bytes_number(struct item_head *ih, int block_size)
{
	return ih_item_len(ih);
}

/* FIXME: this should probably switch to indirect as well */
static void direct_decrement_key(struct cpu_key *key)
{
	cpu_key_k_offset_dec(key);
	if (cpu_key_k_offset(key) == 0)
		set_cpu_key_k_type(key, TYPE_STAT_DATA);
}

static int direct_is_left_mergeable(struct reiserfs_key *key,
				    unsigned long bsize)
{
	int version = le_key_version(key);
	return ((le_key_k_offset(version, key) & (bsize - 1)) != 1);
}

static void direct_print_item(struct item_head *ih, char *item)
{
	int j = 0;

/*    return; */
	printk("\"");
	while (j < ih_item_len(ih))
		printk("%c", item[j++]);
	printk("\"\n");
}

static void direct_check_item(struct item_head *ih, char *item)
{
	/* unused */
}

static int direct_create_vi(struct virtual_node *vn,
			    struct virtual_item *vi,
			    int is_affected, int insert_size)
{
	vi->vi_index = TYPE_DIRECT;
	return 0;
}

static int direct_check_left(struct virtual_item *vi, int free,
			     int start_skip, int end_skip)
{
	int bytes;

	bytes = free - free % 8;
	return bytes ? : -1;
}

static int direct_check_right(struct virtual_item *vi, int free)
{
	return direct_check_left(vi, free, 0, 0);
}

static int direct_part_size(struct virtual_item *vi, int first, int count)
{
	return count;
}

static int direct_unit_num(struct virtual_item *vi)
{
	return vi->vi_item_len - IH_SIZE;
}

static void direct_print_vi(struct virtual_item *vi)
{
	reiserfs_warning(NULL, "reiserfs-16101",
			 "DIRECT, index %d, type 0x%x, %h",
			 vi->vi_index, vi->vi_type, vi->vi_ih);
}

static struct item_operations direct_ops = {
	.bytes_number = direct_bytes_number,
	.decrement_key = direct_decrement_key,
	.is_left_mergeable = direct_is_left_mergeable,
	.print_item = direct_print_item,
	.check_item = direct_check_item,

	.create_vi = direct_create_vi,
	.check_left = direct_check_left,
	.check_right = direct_check_right,
	.part_size = direct_part_size,
	.unit_num = direct_unit_num,
	.print_vi = direct_print_vi
};

/* indirect item functions */
static int indirect_bytes_number(struct item_head *ih, int block_size)
{
	return ih_item_len(ih) / UNFM_P_SIZE * block_size;
}

/* decrease offset, if it becomes 0, change type to stat data */
static void indirect_decrement_key(struct cpu_key *key)
{
	cpu_key_k_offset_dec(key);
	if (cpu_key_k_offset(key) == 0)
		set_cpu_key_k_type(key, TYPE_STAT_DATA);
}

/* if it is not first item of the body, then it is mergeable */
static int indirect_is_left_mergeable(struct reiserfs_key *key,
				      unsigned long bsize)
{
	int version = le_key_version(key);
	return (le_key_k_offset(version, key) != 1);
}

/* printing of indirect item */
static void start_new_sequence(__u32 * start, int *len, __u32 new)
{
	*start = new;
	*len = 1;
}

static int sequence_finished(__u32 start, int *len, __u32 new)
{
	if (start == INT_MAX)
		return 1;

	if (start == 0 && new == 0) {
		(*len)++;
		return 0;
	}
	if (start != 0 && (start + *len) == new) {
		(*len)++;
		return 0;
	}
	return 1;
}

static void print_sequence(__u32 start, int len)
{
	if (start == INT_MAX)
		return;

	if (len == 1)
		printk(" %d", start);
	else
		printk(" %d(%d)", start, len);
}

static void indirect_print_item(struct item_head *ih, char *item)
{
	int j;
	__le32 *unp;
	__u32 prev = INT_MAX;
	int num = 0;

	unp = (__le32 *) item;

	if (ih_item_len(ih) % UNFM_P_SIZE)
		reiserfs_warning(NULL, "reiserfs-16102", "invalid item len");

	printk("%d pointers\n[ ", (int)I_UNFM_NUM(ih));
	for (j = 0; j < I_UNFM_NUM(ih); j++) {
		if (sequence_finished(prev, &num, get_block_num(unp, j))) {
			print_sequence(prev, num);
			start_new_sequence(&prev, &num, get_block_num(unp, j));
		}
	}
	print_sequence(prev, num);
	printk("]\n");
}

static void indirect_check_item(struct item_head *ih, char *item)
{
	/* unused */
}

static int indirect_create_vi(struct virtual_node *vn,
			      struct virtual_item *vi,
			      int is_affected, int insert_size)
{
	vi->vi_index = TYPE_INDIRECT;
	return 0;
}

static int indirect_check_left(struct virtual_item *vi, int free,
			       int start_skip, int end_skip)
{
	int bytes;

	bytes = free - free % UNFM_P_SIZE;
	return bytes ? : -1;
}

static int indirect_check_right(struct virtual_item *vi, int free)
{
	return indirect_check_left(vi, free, 0, 0);
}

/*
 * return size in bytes of 'units' units. If first == 0 - calculate
 * from the head (left), otherwise - from tail (right)
 */
static int indirect_part_size(struct virtual_item *vi, int first, int units)
{
	/* unit of indirect item is byte (yet) */
	return units;
}

static int indirect_unit_num(struct virtual_item *vi)
{
	/* unit of indirect item is byte (yet) */
	return vi->vi_item_len - IH_SIZE;
}

static void indirect_print_vi(struct virtual_item *vi)
{
	reiserfs_warning(NULL, "reiserfs-16103",
			 "INDIRECT, index %d, type 0x%x, %h",
			 vi->vi_index, vi->vi_type, vi->vi_ih);
}

static struct item_operations indirect_ops = {
	.bytes_number = indirect_bytes_number,
	.decrement_key = indirect_decrement_key,
	.is_left_mergeable = indirect_is_left_mergeable,
	.print_item = indirect_print_item,
	.check_item = indirect_check_item,

	.create_vi = indirect_create_vi,
	.check_left = indirect_check_left,
	.check_right = indirect_check_right,
	.part_size = indirect_part_size,
	.unit_num = indirect_unit_num,
	.print_vi = indirect_print_vi
};

/* direntry functions */
static int direntry_bytes_number(struct item_head *ih, int block_size)
{
	reiserfs_warning(NULL, "vs-16090",
			 "bytes number is asked for direntry");
	return 0;
}

static void direntry_decrement_key(struct cpu_key *key)
{
	cpu_key_k_offset_dec(key);
	if (cpu_key_k_offset(key) == 0)
		set_cpu_key_k_type(key, TYPE_STAT_DATA);
}

static int direntry_is_left_mergeable(struct reiserfs_key *key,
				      unsigned long bsize)
{
	if (le32_to_cpu(key->u.k_offset_v1.k_offset) == DOT_OFFSET)
		return 0;
	return 1;

}

static void direntry_print_item(struct item_head *ih, char *item)
{
	int i;
	int namelen;
	struct reiserfs_de_head *deh;
	char *name;
	static char namebuf[80];

	printk("\n # %-15s%-30s%-15s%-15s%-15s\n", "Name",
	       "Key of pointed object", "Hash", "Gen number", "Status");

	deh = (struct reiserfs_de_head *)item;

	for (i = 0; i < ih_entry_count(ih); i++, deh++) {
		namelen =
		    (i ? (deh_location(deh - 1)) : ih_item_len(ih)) -
		    deh_location(deh);
		name = item + deh_location(deh);
		if (name[namelen - 1] == 0)
			namelen = strlen(name);
		namebuf[0] = '"';
		if (namelen > sizeof(namebuf) - 3) {
			strncpy(namebuf + 1, name, sizeof(namebuf) - 3);
			namebuf[sizeof(namebuf) - 2] = '"';
			namebuf[sizeof(namebuf) - 1] = 0;
		} else {
			memcpy(namebuf + 1, name, namelen);
			namebuf[namelen + 1] = '"';
			namebuf[namelen + 2] = 0;
		}

		printk("%d:  %-15s%-15d%-15d%-15lld%-15lld(%s)\n",
		       i, namebuf,
		       deh_dir_id(deh), deh_objectid(deh),
		       GET_HASH_VALUE(deh_offset(deh)),
		       GET_GENERATION_NUMBER((deh_offset(deh))),
		       (de_hidden(deh)) ? "HIDDEN" : "VISIBLE");
	}
}

static void direntry_check_item(struct item_head *ih, char *item)
{
	int i;
	struct reiserfs_de_head *deh;

	/* unused */
	deh = (struct reiserfs_de_head *)item;
	for (i = 0; i < ih_entry_count(ih); i++, deh++) {
		;
	}
}

#define DIRENTRY_VI_FIRST_DIRENTRY_ITEM 1

/*
 * function returns old entry number in directory item in real node
 * using new entry number in virtual item in virtual node
 */
static inline int old_entry_num(int is_affected, int virtual_entry_num,
				int pos_in_item, int mode)
{
	if (mode == M_INSERT || mode == M_DELETE)
		return virtual_entry_num;

	if (!is_affected)
		/* cut or paste is applied to another item */
		return virtual_entry_num;

	if (virtual_entry_num < pos_in_item)
		return virtual_entry_num;

	if (mode == M_CUT)
		return virtual_entry_num + 1;

	RFALSE(mode != M_PASTE || virtual_entry_num == 0,
	       "vs-8015: old_entry_num: mode must be M_PASTE (mode = \'%c\'",
	       mode);

	return virtual_entry_num - 1;
}

/*
 * Create an array of sizes of directory entries for virtual
 * item. Return space used by an item. FIXME: no control over
 * consuming of space used by this item handler
 */
static int direntry_create_vi(struct virtual_node *vn,
			      struct virtual_item *vi,
			      int is_affected, int insert_size)
{
	struct direntry_uarea *dir_u = vi->vi_uarea;
	int i, j;
	int size = sizeof(struct direntry_uarea);
	struct reiserfs_de_head *deh;

	vi->vi_index = TYPE_DIRENTRY;

	BUG_ON(!(vi->vi_ih) || !vi->vi_item);

	dir_u->flags = 0;
	if (le_ih_k_offset(vi->vi_ih) == DOT_OFFSET)
		dir_u->flags |= DIRENTRY_VI_FIRST_DIRENTRY_ITEM;

	deh = (struct reiserfs_de_head *)(vi->vi_item);

	/* virtual directory item have this amount of entry after */
	dir_u->entry_count = ih_entry_count(vi->vi_ih) +
	    ((is_affected) ? ((vn->vn_mode == M_CUT) ? -1 :
			      (vn->vn_mode == M_PASTE ? 1 : 0)) : 0);

	for (i = 0; i < dir_u->entry_count; i++) {
		j = old_entry_num(is_affected, i, vn->vn_pos_in_item,
				  vn->vn_mode);
		dir_u->entry_sizes[i] =
		    (j ? deh_location(&deh[j - 1]) : ih_item_len(vi->vi_ih)) -
		    deh_location(&deh[j]) + DEH_SIZE;
	}

	size += (dir_u->entry_count * sizeof(short));

	/* set size of pasted entry */
	if (is_affected && vn->vn_mode == M_PASTE)
		dir_u->entry_sizes[vn->vn_pos_in_item] = insert_size;

#ifdef CONFIG_REISERFS_CHECK
	/* compare total size of entries with item length */
	{
		int k, l;

		l = 0;
		for (k = 0; k < dir_u->entry_count; k++)
			l += dir_u->entry_sizes[k];

		if (l + IH_SIZE != vi->vi_item_len +
		    ((is_affected
		      && (vn->vn_mode == M_PASTE
			  || vn->vn_mode == M_CUT)) ? insert_size : 0)) {
			reiserfs_panic(NULL, "vs-8025", "(mode==%c, "
				       "insert_size==%d), invalid length of "
				       "directory item",
				       vn->vn_mode, insert_size);
		}
	}
#endif

	return size;

}

/*
 * return number of entries which may fit into specified amount of
 * free space, or -1 if free space is not enough even for 1 entry
 */
static int direntry_check_left(struct virtual_item *vi, int free,
			       int start_skip, int end_skip)
{
	int i;
	int entries = 0;
	struct direntry_uarea *dir_u = vi->vi_uarea;

	for (i = start_skip; i < dir_u->entry_count - end_skip; i++) {
		/* i-th entry doesn't fit into the remaining free space */
		if (dir_u->entry_sizes[i] > free)
			break;

		free -= dir_u->entry_sizes[i];
		entries++;
	}

	if (entries == dir_u->entry_count) {
		reiserfs_panic(NULL, "item_ops-1",
			       "free space %d, entry_count %d", free,
			       dir_u->entry_count);
	}

	/* "." and ".." can not be separated from each other */
	if (start_skip == 0 && (dir_u->flags & DIRENTRY_VI_FIRST_DIRENTRY_ITEM)
	    && entries < 2)
		entries = 0;

	return entries ? : -1;
}

static int direntry_check_right(struct virtual_item *vi, int free)
{
	int i;
	int entries = 0;
	struct direntry_uarea *dir_u = vi->vi_uarea;

	for (i = dir_u->entry_count - 1; i >= 0; i--) {
		/* i-th entry doesn't fit into the remaining free space */
		if (dir_u->entry_sizes[i] > free)
			break;

		free -= dir_u->entry_sizes[i];
		entries++;
	}
	BUG_ON(entries == dir_u->entry_count);

	/* "." and ".." can not be separated from each other */
	if ((dir_u->flags & DIRENTRY_VI_FIRST_DIRENTRY_ITEM)
	    && entries > dir_u->entry_count - 2)
		entries = dir_u->entry_count - 2;

	return entries ? : -1;
}

/* sum of entry sizes between from-th and to-th entries including both edges */
static int direntry_part_size(struct virtual_item *vi, int first, int count)
{
	int i, retval;
	int from, to;
	struct direntry_uarea *dir_u = vi->vi_uarea;

	retval = 0;
	if (first == 0)
		from = 0;
	else
		from = dir_u->entry_count - count;
	to = from + count - 1;

	for (i = from; i <= to; i++)
		retval += dir_u->entry_sizes[i];

	return retval;
}

static int direntry_unit_num(struct virtual_item *vi)
{
	struct direntry_uarea *dir_u = vi->vi_uarea;

	return dir_u->entry_count;
}

static void direntry_print_vi(struct virtual_item *vi)
{
	int i;
	struct direntry_uarea *dir_u = vi->vi_uarea;

	reiserfs_warning(NULL, "reiserfs-16104",
			 "DIRENTRY, index %d, type 0x%x, %h, flags 0x%x",
			 vi->vi_index, vi->vi_type, vi->vi_ih, dir_u->flags);
	printk("%d entries: ", dir_u->entry_count);
	for (i = 0; i < dir_u->entry_count; i++)
		printk("%d ", dir_u->entry_sizes[i]);
	printk("\n");
}

static struct item_operations direntry_ops = {
	.bytes_number = direntry_bytes_number,
	.decrement_key = direntry_decrement_key,
	.is_left_mergeable = direntry_is_left_mergeable,
	.print_item = direntry_print_item,
	.check_item = direntry_check_item,

	.create_vi = direntry_create_vi,
	.check_left = direntry_check_left,
	.check_right = direntry_check_right,
	.part_size = direntry_part_size,
	.unit_num = direntry_unit_num,
	.print_vi = direntry_print_vi
};

/* Error catching functions to catch errors caused by incorrect item types. */
static int errcatch_bytes_number(struct item_head *ih, int block_size)
{
	reiserfs_warning(NULL, "green-16001",
			 "Invalid item type observed, run fsck ASAP");
	return 0;
}

static void errcatch_decrement_key(struct cpu_key *key)
{
	reiserfs_warning(NULL, "green-16002",
			 "Invalid item type observed, run fsck ASAP");
}

static int errcatch_is_left_mergeable(struct reiserfs_key *key,
				      unsigned long bsize)
{
	reiserfs_warning(NULL, "green-16003",
			 "Invalid item type observed, run fsck ASAP");
	return 0;
}

static void errcatch_print_item(struct item_head *ih, char *item)
{
	reiserfs_warning(NULL, "green-16004",
			 "Invalid item type observed, run fsck ASAP");
}

static void errcatch_check_item(struct item_head *ih, char *item)
{
	reiserfs_warning(NULL, "green-16005",
			 "Invalid item type observed, run fsck ASAP");
}

static int errcatch_create_vi(struct virtual_node *vn,
			      struct virtual_item *vi,
			      int is_affected, int insert_size)
{
	reiserfs_warning(NULL, "green-16006",
			 "Invalid item type observed, run fsck ASAP");
	/*
	 * We might return -1 here as well, but it won't help as
	 * create_virtual_node() from where this operation is called
	 * from is of return type void.
	 */
	return 0;
}

static int errcatch_check_left(struct virtual_item *vi, int free,
			       int start_skip, int end_skip)
{
	reiserfs_warning(NULL, "green-16007",
			 "Invalid item type observed, run fsck ASAP");
	return -1;
}

static int errcatch_check_right(struct virtual_item *vi, int free)
{
	reiserfs_warning(NULL, "green-16008",
			 "Invalid item type observed, run fsck ASAP");
	return -1;
}

static int errcatch_part_size(struct virtual_item *vi, int first, int count)
{
	reiserfs_warning(NULL, "green-16009",
			 "Invalid item type observed, run fsck ASAP");
	return 0;
}

static int errcatch_unit_num(struct virtual_item *vi)
{
	reiserfs_warning(NULL, "green-16010",
			 "Invalid item type observed, run fsck ASAP");
	return 0;
}

static void errcatch_print_vi(struct virtual_item *vi)
{
	reiserfs_warning(NULL, "green-16011",
			 "Invalid item type observed, run fsck ASAP");
}

static struct item_operations errcatch_ops = {
	.bytes_number = errcatch_bytes_number,
	.decrement_key = errcatch_decrement_key,
	.is_left_mergeable = errcatch_is_left_mergeable,
	.print_item = errcatch_print_item,
	.check_item = errcatch_check_item,

	.create_vi = errcatch_create_vi,
	.check_left = errcatch_check_left,
	.check_right = errcatch_check_right,
	.part_size = errcatch_part_size,
	.unit_num = errcatch_unit_num,
	.print_vi = errcatch_print_vi
};

#if ! (TYPE_STAT_DATA == 0 && TYPE_INDIRECT == 1 && TYPE_DIRECT == 2 && TYPE_DIRENTRY == 3)
#error Item types must use disk-format assigned values.
#endif

struct item_operations *item_ops[TYPE_ANY + 1] = {
	&stat_data_ops,
	&indirect_ops,
	&direct_ops,
	&direntry_ops,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	&errcatch_ops		/* This is to catch errors with invalid type (15th entry for TYPE_ANY) */
};