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
|
/*
* res_gdt.c --- reserve blocks for growing the group descriptor table
* during online resizing.
*
* Copyright (C) 2002 Andreas Dilger
*
* %Begin-Header%
* This file may be redistributed under the terms of the GNU Library
* General Public License, version 2.
* %End-Header%
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "ext2_fs.h"
#include "ext2fs.h"
/*
* Iterate through the groups which hold BACKUP superblock/GDT copies in an
* ext3 filesystem. The counters should be initialized to 1, 5, and 7 before
* calling this for the first time. In a sparse filesystem it will be the
* sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ...
* For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ...
*/
static unsigned int list_backups(ext2_filsys fs, unsigned int *three,
unsigned int *five, unsigned int *seven)
{
unsigned int *min = three;
int mult = 3;
unsigned int ret;
if (ext2fs_has_feature_sparse_super2(fs->super)) {
if (*min == 1) {
*min += 1;
if (fs->super->s_backup_bgs[0])
return fs->super->s_backup_bgs[0];
}
if (*min == 2) {
*min += 1;
if (fs->super->s_backup_bgs[1])
return fs->super->s_backup_bgs[1];
}
return fs->group_desc_count;
}
if (!ext2fs_has_feature_sparse_super(fs->super)) {
ret = *min;
*min += 1;
return ret;
}
if (*five < *min) {
min = five;
mult = 5;
}
if (*seven < *min) {
min = seven;
mult = 7;
}
ret = *min;
*min *= mult;
return ret;
}
/*
* This code assumes that the reserved blocks have already been marked in-use
* during ext2fs_initialize(), so that they are not allocated for other
* uses before we can add them to the resize inode (which has to come
* after the creation of the inode table).
*/
errcode_t ext2fs_create_resize_inode(ext2_filsys fs)
{
errcode_t retval, retval2;
struct ext2_super_block *sb;
struct ext2_inode inode;
__u32 *dindir_buf, *gdt_buf;
unsigned long long apb, inode_size;
/* FIXME-64 - can't deal with extents */
blk_t dindir_blk, rsv_off, gdt_off, gdt_blk;
int dindir_dirty = 0, inode_dirty = 0, sb_blk = 0;
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
sb = fs->super;
retval = ext2fs_get_array(2, fs->blocksize, &dindir_buf);
if (retval)
return retval;
gdt_buf = (__u32 *)((char *)dindir_buf + fs->blocksize);
retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
if (retval)
goto out_free;
/*
* File systems with a blocksize of 1024 and bigalloc have
* sb->s_first_data_block of 0; yet the superblock is still at
* block #1. We compensate for it here.
*/
sb_blk = sb->s_first_data_block;
if (fs->blocksize == 1024 && sb_blk == 0)
sb_blk = 1;
/* Maximum possible file size (we only use double indirect blocks) */
apb = EXT2_ADDR_PER_BLOCK(sb);
if ((dindir_blk = inode.i_block[EXT2_DIND_BLOCK])) {
#ifdef RES_GDT_DEBUG
printf("reading GDT dindir %u\n", dindir_blk);
#endif
retval = ext2fs_read_ind_block(fs, dindir_blk, dindir_buf);
if (retval)
goto out_inode;
} else {
blk_t goal = sb_blk + fs->desc_blocks +
sb->s_reserved_gdt_blocks + 2 +
fs->inode_blocks_per_group;
retval = ext2fs_alloc_block(fs, goal, 0, &dindir_blk);
if (retval)
goto out_free;
inode.i_mode = LINUX_S_IFREG | 0600;
inode.i_links_count = 1;
inode.i_block[EXT2_DIND_BLOCK] = dindir_blk;
ext2fs_iblk_set(fs, &inode, 1);
memset(dindir_buf, 0, fs->blocksize);
#ifdef RES_GDT_DEBUG
printf("allocated GDT dindir %u\n", dindir_blk);
#endif
dindir_dirty = inode_dirty = 1;
inode_size = apb*apb + apb + EXT2_NDIR_BLOCKS;
inode_size *= fs->blocksize;
retval = ext2fs_inode_size_set(fs, &inode, inode_size);
if (retval)
goto out_free;
inode.i_ctime = fs->now ? fs->now : time(0);
}
for (rsv_off = 0, gdt_off = fs->desc_blocks,
gdt_blk = sb_blk + 1 + fs->desc_blocks;
rsv_off < sb->s_reserved_gdt_blocks;
rsv_off++, gdt_off++, gdt_blk++) {
unsigned int three = 1, five = 5, seven = 7;
unsigned int grp, last = 0;
int gdt_dirty = 0;
gdt_off %= apb;
if (!dindir_buf[gdt_off]) {
/* FIXME XXX XXX
blk_t new_blk;
retval = ext2fs_new_block(fs, gdt_blk, 0, &new_blk);
if (retval)
goto out_free;
if (new_blk != gdt_blk) {
// XXX free block
retval = -1; // XXX
}
*/
gdt_dirty = dindir_dirty = inode_dirty = 1;
memset(gdt_buf, 0, fs->blocksize);
dindir_buf[gdt_off] = gdt_blk;
ext2fs_iblk_add_blocks(fs, &inode, 1);
#ifdef RES_GDT_DEBUG
printf("added primary GDT block %u at %u[%u]\n",
gdt_blk, dindir_blk, gdt_off);
#endif
} else if (dindir_buf[gdt_off] == gdt_blk) {
#ifdef RES_GDT_DEBUG
printf("reading primary GDT block %u\n", gdt_blk);
#endif
retval = ext2fs_read_ind_block(fs, gdt_blk, gdt_buf);
if (retval)
goto out_dindir;
} else {
#ifdef RES_GDT_DEBUG
printf("bad primary GDT %u != %u at %u[%u]\n",
dindir_buf[gdt_off], gdt_blk,dindir_blk,gdt_off);
#endif
retval = EXT2_ET_RESIZE_INODE_CORRUPT;
goto out_dindir;
}
while ((grp = list_backups(fs, &three, &five, &seven)) <
fs->group_desc_count) {
blk_t expect = gdt_blk + grp * sb->s_blocks_per_group;
if (!gdt_buf[last]) {
#ifdef RES_GDT_DEBUG
printf("added backup GDT %u grp %u@%u[%u]\n",
expect, grp, gdt_blk, last);
#endif
gdt_buf[last] = expect;
ext2fs_iblk_add_blocks(fs, &inode, 1);
gdt_dirty = inode_dirty = 1;
} else if (gdt_buf[last] != expect) {
#ifdef RES_GDT_DEBUG
printf("bad backup GDT %u != %u at %u[%u]\n",
gdt_buf[last], expect, gdt_blk, last);
#endif
retval = EXT2_ET_RESIZE_INODE_CORRUPT;
goto out_dindir;
}
last++;
}
if (gdt_dirty) {
#ifdef RES_GDT_DEBUG
printf("writing primary GDT block %u\n", gdt_blk);
#endif
retval = ext2fs_write_ind_block(fs, gdt_blk, gdt_buf);
if (retval)
goto out_dindir;
}
}
out_dindir:
if (dindir_dirty) {
retval2 = ext2fs_write_ind_block(fs, dindir_blk, dindir_buf);
if (!retval)
retval = retval2;
}
out_inode:
#ifdef RES_GDT_DEBUG
printf("inode.i_blocks = %u, i_size = %lu\n", inode.i_blocks,
EXT2_I_SIZE(&inode));
#endif
if (inode_dirty) {
inode.i_atime = inode.i_mtime = fs->now ? fs->now : time(0);
retval2 = ext2fs_write_new_inode(fs, EXT2_RESIZE_INO, &inode);
if (!retval)
retval = retval2;
}
out_free:
ext2fs_free_mem(&dindir_buf);
return retval;
}
|