ext4_journal: fix ext4_block not being freed.
[lwext4.git] / lwext4 / ext4_journal.c
1 /**
2  * @file  ext4_journal.c
3  * @brief Journalling
4  */
5
6 #include "ext4_config.h"
7 #include "ext4_types.h"
8 #include "ext4_fs.h"
9 #include "ext4_super.h"
10 #include "ext4_errno.h"
11 #include "ext4_blockdev.h"
12 #include "ext4_crc32c.h"
13 #include "ext4_debug.h"
14
15 #include <string.h>
16
17 int jbd_inode_bmap(struct jbd_fs *jbd_fs,
18                    ext4_lblk_t iblock,
19                    ext4_fsblk_t *fblock);
20
21 int jbd_sb_write(struct jbd_fs *jbd_fs, struct jbd_sb *s)
22 {
23         int rc;
24         struct ext4_fs *fs = jbd_fs->inode_ref.fs;
25         uint64_t offset;
26         ext4_fsblk_t fblock;
27         rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
28         if (rc != EOK)
29                 return rc;
30
31         offset = fblock * ext4_sb_get_block_size(&fs->sb);
32         return ext4_block_writebytes(fs->bdev, offset, s,
33                                      EXT4_SUPERBLOCK_SIZE);
34 }
35
36 int jbd_sb_read(struct jbd_fs *jbd_fs, struct jbd_sb *s)
37 {
38         int rc;
39         struct ext4_fs *fs = jbd_fs->inode_ref.fs;
40         uint64_t offset;
41         ext4_fsblk_t fblock;
42         rc = jbd_inode_bmap(jbd_fs, 0, &fblock);
43         if (rc != EOK)
44                 return rc;
45
46         offset = fblock * ext4_sb_get_block_size(&fs->sb);
47         return ext4_block_readbytes(fs->bdev, offset, s,
48                                     EXT4_SUPERBLOCK_SIZE);
49 }
50
51 static bool jbd_verify_sb(struct jbd_sb *sb)
52 {
53         struct jbd_bhdr *bhdr = &sb->header;
54         if (bhdr->magic != to_be32(JBD_MAGIC_NUMBER))
55                 return false;
56
57         if (bhdr->blocktype != to_be32(JBD_SUPERBLOCK) &&
58             bhdr->blocktype != to_be32(JBD_SUPERBLOCK_V2))
59                 return false;
60
61         return true;
62 }
63
64 int jbd_get_fs(struct ext4_fs *fs,
65                struct jbd_fs *jbd_fs)
66 {
67         int rc;
68         uint32_t journal_ino;
69
70         memset(jbd_fs, 0, sizeof(struct jbd_fs));
71         journal_ino = ext4_get32(&fs->sb, journal_inode_number);
72
73         rc = ext4_fs_get_inode_ref(fs,
74                                    journal_ino,
75                                    &jbd_fs->inode_ref);
76         if (rc != EOK) {
77                 memset(jbd_fs, 0, sizeof(struct jbd_fs));
78                 return rc;
79         }
80         rc = jbd_sb_read(jbd_fs, &jbd_fs->sb);
81         if (rc != EOK) {
82                 memset(jbd_fs, 0, sizeof(struct jbd_fs));
83                 ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
84         }
85
86         return rc;
87 }
88
89 int jbd_put_fs(struct jbd_fs *jbd_fs)
90 {
91         int rc;
92         rc = ext4_fs_put_inode_ref(&jbd_fs->inode_ref);
93         return rc;
94 }
95
96 int jbd_inode_bmap(struct jbd_fs *jbd_fs,
97                    ext4_lblk_t iblock,
98                    ext4_fsblk_t *fblock)
99 {
100         int rc = ext4_fs_get_inode_data_block_index(
101                         &jbd_fs->inode_ref,
102                         iblock,
103                         fblock,
104                         false);
105         return rc;
106 }
107
108 int jbd_block_get(struct jbd_fs *jbd_fs,
109                   struct ext4_block *block,
110                   ext4_fsblk_t fblock)
111 {
112         /* TODO: journal device. */
113         int rc;
114         ext4_lblk_t iblock = (ext4_lblk_t)fblock;
115         rc = jbd_inode_bmap(jbd_fs, iblock,
116                             &fblock);
117         if (rc != EOK)
118                 return rc;
119
120         struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
121         rc = ext4_block_get(bdev, block, fblock);
122         return rc;
123 }
124
125 int jbd_block_get_noread(struct jbd_fs *jbd_fs,
126                          struct ext4_block *block,
127                          ext4_fsblk_t fblock)
128 {
129         /* TODO: journal device. */
130         int rc;
131         ext4_lblk_t iblock = (ext4_lblk_t)fblock;
132         rc = jbd_inode_bmap(jbd_fs, iblock,
133                             &fblock);
134         if (rc != EOK)
135                 return rc;
136
137         struct ext4_blockdev *bdev = jbd_fs->inode_ref.fs->bdev;
138         rc = ext4_block_get_noread(bdev, block, fblock);
139         return rc;
140 }
141
142 int jbd_block_set(struct jbd_fs *jbd_fs,
143                   struct ext4_block *block)
144 {
145         return ext4_block_set(jbd_fs->inode_ref.fs->bdev,
146                               block);
147 }
148
149 /* Make sure we wrap around the log correctly! */
150 #define wrap(sb, var)                                           \
151 do {                                                                    \
152         if (var >= to_be32((sb)->maxlen))                                       \
153                 var -= (to_be32((sb)->maxlen) - to_be32((sb)->first));  \
154 } while (0)
155
156 #define ACTION_SCAN 0
157 #define ACTION_REVOKE 1
158 #define ACTION_RECOVER 2
159
160 struct recover_info {
161         uint32_t start_trans_id;
162         uint32_t last_trans_id;
163 };
164
165 int jbd_iterate_log(struct jbd_fs *jbd_fs,
166                     struct recover_info *info,
167                     int action)
168 {
169         int r = EOK;
170         bool log_end = false;
171         struct jbd_sb *sb = &jbd_fs->sb;
172         uint32_t start_trans_id, this_trans_id;
173         uint32_t start_block, this_block;
174
175         start_trans_id = this_trans_id = to_be32(sb->sequence);
176         start_block = this_block = to_be32(sb->start);
177
178         ext4_dbg(DEBUG_JBD, "Start of journal at trans id: %" PRIu32 "\n",
179                             start_trans_id);
180
181         while (!log_end) {
182                 struct ext4_block block;
183                 struct jbd_bhdr *header;
184                 if (action != ACTION_SCAN)
185                         if (this_trans_id > info->last_trans_id) {
186                                 log_end = true;
187                                 continue;
188                         }
189
190                 r = jbd_block_get(jbd_fs, &block, this_block);
191                 if (r != EOK)
192                         break;
193
194                 header = (struct jbd_bhdr *)block.data;
195                 if (header->magic != to_be32(JBD_MAGIC_NUMBER)) {
196                         jbd_block_set(jbd_fs, &block);
197                         log_end = true;
198                         continue;
199                 }
200
201                 if (header->sequence != to_be32(this_trans_id)) {
202                         if (this_trans_id <= info->last_trans_id)
203                                 r = EIO;
204
205                         jbd_block_set(jbd_fs, &block);
206                         log_end = true;
207                         continue;
208                 }
209
210                 switch (header->blocktype) {
211                 case JBD_DESCRIPTOR_BLOCK:
212                         ext4_dbg(DEBUG_JBD, "Descriptor block: %u, "
213                                             "trans_id: %u\n",
214                                             this_block, this_trans_id);
215                         break;
216                 case JBD_COMMIT_BLOCK:
217                         ext4_dbg(DEBUG_JBD, "Commit block: %u, "
218                                             "trans_id: %u\n",
219                                             this_block, this_trans_id);
220                         this_trans_id++;
221                         break;
222                 case JBD_REVOKE_BLOCK:
223                         ext4_dbg(DEBUG_JBD, "Revoke block: %u, "
224                                             "trans_id: %u\n",
225                                             this_block, this_trans_id);
226                         break;
227                 default:
228                         log_end = true;
229                         break;
230                 }
231                 jbd_block_set(jbd_fs, &block);
232                 this_block++;
233                 wrap(sb, this_block);
234                 if (this_block == start_block)
235                         log_end = true;
236
237         }
238         ext4_dbg(DEBUG_JBD, "End of journal.\n");
239         if (r == EOK && action == ACTION_SCAN) {
240                 info->start_trans_id = start_trans_id;
241                 if (this_trans_id > start_trans_id)
242                         info->last_trans_id = this_trans_id - 1;
243                 else
244                         info->last_trans_id = this_trans_id;
245         }
246
247         return r;
248 }
249
250 int jbd_recover(struct jbd_fs *jbd_fs)
251 {
252         int r;
253         struct recover_info info;
254         struct jbd_sb *sb = &jbd_fs->sb;
255         if (!sb->start)
256                 return EOK;
257
258         r = jbd_iterate_log(jbd_fs, &info, ACTION_SCAN);
259         return r;
260 }