Whamcloud - gitweb
Merge branch 'maint' into next
[tools/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_new_inode(fs, EXT2_ROOT_INO, LINUX_S_IFREG | 0600,
132                                        0, &ino);
133                 if (err)
134                         return err;
135                 ext2fs_inode_alloc_stats2(fs, ino, +1, 0);
136                 ext2fs_mark_ib_dirty(fs);
137         }
138
139         err = ext2fs_read_inode(fs, ino, &inode);
140         if (err)
141                 return err;
142         if (EXT2_I_SIZE(&inode)) {
143                 err = ext2fs_truncate_orphan_file(fs);
144                 if (err)
145                         return err;
146         }
147
148         memset(&inode, 0, sizeof(struct ext2_inode));
149         if (ext2fs_has_feature_extents(fs->super)) {
150                 inode.i_flags |= EXT4_EXTENTS_FL;
151                 err = ext2fs_write_inode(fs, ino, &inode);
152                 if (err)
153                         return err;
154         }
155
156         err = ext2fs_get_mem(fs->blocksize, &buf);
157         if (err)
158                 return err;
159         err = ext2fs_get_mem(fs->blocksize, &zerobuf);
160         if (err)
161                 goto out;
162         memset(buf, 0, fs->blocksize);
163         memset(zerobuf, 0, fs->blocksize);
164         ob_tail = ext2fs_orphan_block_tail(fs, buf);
165         ob_tail->ob_magic = ext2fs_cpu_to_le32(EXT4_ORPHAN_BLOCK_MAGIC);
166         oi.num_blocks = num_blocks;
167         oi.alloc_blocks = 0;
168         oi.last_blk = 0;
169         oi.generation = inode.i_generation;
170         oi.ino = ino;
171         oi.buf = buf;
172         oi.zerobuf = zerobuf;
173         oi.err = 0;
174         err = ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_APPEND,
175                                     0, mkorphan_proc, &oi);
176         if (err)
177                 goto out;
178         if (oi.err) {
179                 err = oi.err;
180                 goto out;
181         }
182
183         /* Reread inode after blocks were allocated */
184         err = ext2fs_read_inode(fs, ino, &inode);
185         if (err)
186                 goto out;
187         ext2fs_iblk_set(fs, &inode, 0);
188         inode.i_atime = inode.i_mtime =
189                 inode.i_ctime = fs->now ? fs->now : time(0);
190         inode.i_links_count = 1;
191         inode.i_mode = LINUX_S_IFREG | 0600;
192         ext2fs_iblk_add_blocks(fs, &inode, oi.alloc_blocks);
193         err = ext2fs_inode_size_set(fs, &inode,
194                         (unsigned long long)fs->blocksize * num_blocks);
195         if (err)
196                 goto out;
197         err = ext2fs_write_new_inode(fs, ino, &inode);
198         if (err)
199                 goto out;
200
201         fs->super->s_orphan_file_inum = ino;
202         ext2fs_set_feature_orphan_file(fs->super);
203         ext2fs_mark_super_dirty(fs);
204         /* Need to update group descriptors as well */
205         fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
206 out:
207         if (buf)
208                 ext2fs_free_mem(&buf);
209         if (zerobuf)
210                 ext2fs_free_mem(&zerobuf);
211         return err;
212 }
213
214 /*
215  * Find reasonable size for orphan file. We choose orphan file size to be
216  * between 32 and 512 filesystem blocks and not more than 1/4096 of the
217  * filesystem unless it is really small.
218  */
219 e2_blkcnt_t ext2fs_default_orphan_file_blocks(ext2_filsys fs)
220 {
221         __u64 num_blocks = ext2fs_blocks_count(fs->super);
222         e2_blkcnt_t blks = 512;
223
224         if (num_blocks < 128 * 1024)
225                 blks = 32;
226         else if (num_blocks < 2 * 1024 * 1024)
227                 blks = num_blocks / 4096;
228         return (blks + EXT2FS_CLUSTER_MASK(fs)) & ~EXT2FS_CLUSTER_MASK(fs);
229 }
230
231 static errcode_t ext2fs_orphan_file_block_csum(ext2_filsys fs, ext2_ino_t ino,
232                                                blk64_t blk, char *buf,
233                                                __u32 *crcp)
234 {
235         struct ext2_inode inode;
236         errcode_t retval;
237
238         retval = ext2fs_read_inode(fs, ino, &inode);
239         if (retval)
240                 return retval;
241         *crcp = ext2fs_do_orphan_file_block_csum(fs, ino, inode.i_generation,
242                                                  blk, buf);
243         return 0;
244 }
245
246 errcode_t ext2fs_orphan_file_block_csum_set(ext2_filsys fs, ext2_ino_t ino,
247                                             blk64_t blk, char *buf)
248 {
249         struct ext4_orphan_block_tail *tail;
250
251         if (!ext2fs_has_feature_metadata_csum(fs->super))
252                 return 0;
253
254         tail = ext2fs_orphan_block_tail(fs, buf);
255         return ext2fs_orphan_file_block_csum(fs, ino, blk, buf,
256                                              &tail->ob_checksum);
257 }
258
259 int ext2fs_orphan_file_block_csum_verify(ext2_filsys fs, ext2_ino_t ino,
260                                          blk64_t blk, char *buf)
261 {
262         struct ext4_orphan_block_tail *tail;
263         __u32 crc;
264         errcode_t retval;
265
266         if (!ext2fs_has_feature_metadata_csum(fs->super))
267                 return 1;
268         retval = ext2fs_orphan_file_block_csum(fs, ino, blk, buf, &crc);
269         if (retval)
270                 return 0;
271         tail = ext2fs_orphan_block_tail(fs, buf);
272         return ext2fs_le32_to_cpu(tail->ob_checksum) == crc;
273 }