]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blob - lib/ext2fs/orphan.c
libext2fs: don't truncate the orphan file inode if it is newly allocated
[thirdparty/e2fsprogs.git] / lib / ext2fs / orphan.c
1 /*
2 * orphan.c --- utility function to handle orphan file
3 *
4 * Copyright (C) 2015 Jan Kara.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Library
8 * General Public License, version 2.
9 * %End-Header%
10 */
11
12 #include "config.h"
13 #include <string.h>
14
15 #include "ext2_fs.h"
16 #include "ext2fsP.h"
17
18 errcode_t ext2fs_truncate_orphan_file(ext2_filsys fs)
19 {
20 struct ext2_inode inode;
21 errcode_t err;
22 ext2_ino_t ino = fs->super->s_orphan_file_inum;
23
24 err = ext2fs_read_inode(fs, ino, &inode);
25 if (err)
26 return err;
27
28 err = ext2fs_punch(fs, ino, &inode, NULL, 0, ~0ULL);
29 if (err)
30 return err;
31
32 fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
33 memset(&inode, 0, sizeof(struct ext2_inode));
34 err = ext2fs_write_inode(fs, ino, &inode);
35
36 ext2fs_clear_feature_orphan_file(fs->super);
37 ext2fs_clear_feature_orphan_present(fs->super);
38 ext2fs_mark_super_dirty(fs);
39 /* Need to update group descriptors as well */
40 fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
41
42 return err;
43 }
44
45 __u32 ext2fs_do_orphan_file_block_csum(ext2_filsys fs, ext2_ino_t ino,
46 __u32 gen, blk64_t blk, char *buf)
47 {
48 int inodes_per_ob = ext2fs_inodes_per_orphan_block(fs);
49 __u32 crc;
50
51 ino = ext2fs_cpu_to_le32(ino);
52 gen = ext2fs_cpu_to_le32(gen);
53 blk = ext2fs_cpu_to_le64(blk);
54 crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&ino,
55 sizeof(ino));
56 crc = ext2fs_crc32c_le(crc, (unsigned char *)&gen, sizeof(gen));
57 crc = ext2fs_crc32c_le(crc, (unsigned char *)&blk, sizeof(blk));
58 crc = ext2fs_crc32c_le(crc, (unsigned char *)buf,
59 inodes_per_ob * sizeof(__u32));
60
61 return ext2fs_cpu_to_le32(crc);
62 }
63
64 struct mkorphan_info {
65 char *buf;
66 char *zerobuf;
67 blk_t num_blocks;
68 blk_t alloc_blocks;
69 blk64_t last_blk;
70 errcode_t err;
71 ext2_ino_t ino;
72 __u32 generation;
73 };
74
75 static int mkorphan_proc(ext2_filsys fs,
76 blk64_t *blocknr,
77 e2_blkcnt_t blockcnt,
78 blk64_t ref_block EXT2FS_ATTR((unused)),
79 int ref_offset EXT2FS_ATTR((unused)),
80 void *priv_data)
81 {
82 struct mkorphan_info *oi = (struct mkorphan_info *)priv_data;
83 blk64_t new_blk;
84 errcode_t err;
85
86 /* Can we just continue in currently allocated cluster? */
87 if (blockcnt &&
88 EXT2FS_B2C(fs, oi->last_blk) == EXT2FS_B2C(fs, oi->last_blk + 1)) {
89 new_blk = oi->last_blk + 1;
90 } else {
91 err = ext2fs_new_block2(fs, oi->last_blk, 0, &new_blk);
92 if (err) {
93 oi->err = err;
94 return BLOCK_ABORT;
95 }
96 ext2fs_block_alloc_stats2(fs, new_blk, +1);
97 oi->alloc_blocks++;
98 }
99 if (blockcnt >= 0) {
100 if (ext2fs_has_feature_metadata_csum(fs->super)) {
101 struct ext4_orphan_block_tail *tail;
102
103 tail = ext2fs_orphan_block_tail(fs, oi->buf);
104 tail->ob_checksum = ext2fs_do_orphan_file_block_csum(fs,
105 oi->ino, oi->generation, new_blk, oi->buf);
106 }
107 err = io_channel_write_blk64(fs->io, new_blk, 1, oi->buf);
108 } else /* zerobuf is used to initialize new indirect blocks... */
109 err = io_channel_write_blk64(fs->io, new_blk, 1, oi->zerobuf);
110 if (err) {
111 oi->err = err;
112 return BLOCK_ABORT;
113 }
114 oi->last_blk = new_blk;
115 *blocknr = new_blk;
116 if (blockcnt >= 0 && --oi->num_blocks == 0)
117 return BLOCK_CHANGED | BLOCK_ABORT;
118 return BLOCK_CHANGED;
119 }
120
121 errcode_t ext2fs_create_orphan_file(ext2_filsys fs, blk_t num_blocks)
122 {
123 struct ext2_inode inode;
124 ext2_ino_t ino = fs->super->s_orphan_file_inum;
125 errcode_t err;
126 char *buf = NULL, *zerobuf = NULL;
127 struct mkorphan_info oi;
128 struct ext4_orphan_block_tail *ob_tail;
129
130 if (ino) {
131 err = ext2fs_read_inode(fs, ino, &inode);
132 if (err)
133 return err;
134 if (EXT2_I_SIZE(&inode)) {
135 err = ext2fs_truncate_orphan_file(fs);
136 if (err)
137 return err;
138 }
139 } else {
140 err = ext2fs_new_inode(fs, EXT2_ROOT_INO, LINUX_S_IFREG | 0600,
141 0, &ino);
142 if (err)
143 return err;
144 ext2fs_inode_alloc_stats2(fs, ino, +1, 0);
145 }
146
147 memset(&inode, 0, sizeof(struct ext2_inode));
148 if (ext2fs_has_feature_extents(fs->super)) {
149 inode.i_flags |= EXT4_EXTENTS_FL;
150 err = ext2fs_write_inode(fs, ino, &inode);
151 if (err)
152 return err;
153 }
154
155 err = ext2fs_get_mem(fs->blocksize, &buf);
156 if (err)
157 return err;
158 err = ext2fs_get_mem(fs->blocksize, &zerobuf);
159 if (err)
160 goto out;
161 memset(buf, 0, fs->blocksize);
162 memset(zerobuf, 0, fs->blocksize);
163 ob_tail = ext2fs_orphan_block_tail(fs, buf);
164 ob_tail->ob_magic = ext2fs_cpu_to_le32(EXT4_ORPHAN_BLOCK_MAGIC);
165 oi.num_blocks = num_blocks;
166 oi.alloc_blocks = 0;
167 oi.last_blk = 0;
168 oi.generation = inode.i_generation;
169 oi.ino = ino;
170 oi.buf = buf;
171 oi.zerobuf = zerobuf;
172 oi.err = 0;
173 err = ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_APPEND,
174 0, mkorphan_proc, &oi);
175 if (err)
176 goto out;
177 if (oi.err) {
178 err = oi.err;
179 goto out;
180 }
181
182 /* Reread inode after blocks were allocated */
183 err = ext2fs_read_inode(fs, ino, &inode);
184 if (err)
185 goto out;
186 ext2fs_iblk_set(fs, &inode, 0);
187 inode.i_atime = inode.i_mtime =
188 inode.i_ctime = fs->now ? fs->now : time(0);
189 inode.i_links_count = 1;
190 inode.i_mode = LINUX_S_IFREG | 0600;
191 ext2fs_iblk_add_blocks(fs, &inode, oi.alloc_blocks);
192 err = ext2fs_inode_size_set(fs, &inode,
193 (unsigned long long)fs->blocksize * num_blocks);
194 if (err)
195 goto out;
196 err = ext2fs_write_new_inode(fs, ino, &inode);
197 if (err)
198 goto out;
199
200 fs->super->s_orphan_file_inum = ino;
201 ext2fs_set_feature_orphan_file(fs->super);
202 ext2fs_mark_super_dirty(fs);
203 /* Need to update group descriptors as well */
204 fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
205 out:
206 if (buf)
207 ext2fs_free_mem(&buf);
208 if (zerobuf)
209 ext2fs_free_mem(&zerobuf);
210 return err;
211 }
212
213 /*
214 * Find reasonable size for orphan file. We choose orphan file size to be
215 * between 32 and 512 filesystem blocks and not more than 1/4096 of the
216 * filesystem unless it is really small.
217 */
218 e2_blkcnt_t ext2fs_default_orphan_file_blocks(ext2_filsys fs)
219 {
220 __u64 num_blocks = ext2fs_blocks_count(fs->super);
221 e2_blkcnt_t blks = 512;
222
223 if (num_blocks < 128 * 1024)
224 blks = 32;
225 else if (num_blocks < 2 * 1024 * 1024)
226 blks = num_blocks / 4096;
227 return (blks + EXT2FS_CLUSTER_MASK(fs)) & ~EXT2FS_CLUSTER_MASK(fs);
228 }
229
230 static errcode_t ext2fs_orphan_file_block_csum(ext2_filsys fs, ext2_ino_t ino,
231 blk64_t blk, char *buf,
232 __u32 *crcp)
233 {
234 struct ext2_inode inode;
235 errcode_t retval;
236
237 retval = ext2fs_read_inode(fs, ino, &inode);
238 if (retval)
239 return retval;
240 *crcp = ext2fs_do_orphan_file_block_csum(fs, ino, inode.i_generation,
241 blk, buf);
242 return 0;
243 }
244
245 errcode_t ext2fs_orphan_file_block_csum_set(ext2_filsys fs, ext2_ino_t ino,
246 blk64_t blk, char *buf)
247 {
248 struct ext4_orphan_block_tail *tail;
249
250 if (!ext2fs_has_feature_metadata_csum(fs->super))
251 return 0;
252
253 tail = ext2fs_orphan_block_tail(fs, buf);
254 return ext2fs_orphan_file_block_csum(fs, ino, blk, buf,
255 &tail->ob_checksum);
256 }
257
258 int ext2fs_orphan_file_block_csum_verify(ext2_filsys fs, ext2_ino_t ino,
259 blk64_t blk, char *buf)
260 {
261 struct ext4_orphan_block_tail *tail;
262 __u32 crc;
263 errcode_t retval;
264
265 if (!ext2fs_has_feature_metadata_csum(fs->super))
266 return 1;
267 retval = ext2fs_orphan_file_block_csum(fs, ino, blk, buf, &crc);
268 if (retval)
269 return 0;
270 tail = ext2fs_orphan_block_tail(fs, buf);
271 return ext2fs_le32_to_cpu(tail->ob_checksum) == crc;
272 }