/src/util-linux/libblkid/src/superblocks/ext.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 1999, 2001 by Andries Brouwer |
3 | | * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o |
4 | | * Copyright (C) 2008 Karel Zak <kzak@redhat.com> |
5 | | * |
6 | | * This file may be redistributed under the terms of the |
7 | | * GNU Lesser General Public License. |
8 | | */ |
9 | | #include <stdio.h> |
10 | | #include <stdlib.h> |
11 | | #include <unistd.h> |
12 | | #include <string.h> |
13 | | #include <errno.h> |
14 | | #include <ctype.h> |
15 | | #include <stdint.h> |
16 | | #ifdef __linux__ |
17 | | #include <sys/utsname.h> |
18 | | #endif |
19 | | #include <time.h> |
20 | | |
21 | | #include "superblocks.h" |
22 | | #include "crc32c.h" |
23 | | |
24 | | struct ext2_super_block { |
25 | | uint32_t s_inodes_count; |
26 | | uint32_t s_blocks_count; |
27 | | uint32_t s_r_blocks_count; |
28 | | uint32_t s_free_blocks_count; |
29 | | uint32_t s_free_inodes_count; |
30 | | uint32_t s_first_data_block; |
31 | | uint32_t s_log_block_size; |
32 | | uint32_t s_dummy3[7]; |
33 | | unsigned char s_magic[2]; |
34 | | uint16_t s_state; |
35 | | uint16_t s_errors; |
36 | | uint16_t s_minor_rev_level; |
37 | | uint32_t s_lastcheck; |
38 | | uint32_t s_checkinterval; |
39 | | uint32_t s_creator_os; |
40 | | uint32_t s_rev_level; |
41 | | uint16_t s_def_resuid; |
42 | | uint16_t s_def_resgid; |
43 | | uint32_t s_first_ino; |
44 | | uint16_t s_inode_size; |
45 | | uint16_t s_block_group_nr; |
46 | | uint32_t s_feature_compat; |
47 | | uint32_t s_feature_incompat; |
48 | | uint32_t s_feature_ro_compat; |
49 | | unsigned char s_uuid[16]; |
50 | | char s_volume_name[16]; |
51 | | char s_last_mounted[64]; |
52 | | uint32_t s_algorithm_usage_bitmap; |
53 | | uint8_t s_prealloc_blocks; |
54 | | uint8_t s_prealloc_dir_blocks; |
55 | | uint16_t s_reserved_gdt_blocks; |
56 | | uint8_t s_journal_uuid[16]; |
57 | | uint32_t s_journal_inum; |
58 | | uint32_t s_journal_dev; |
59 | | uint32_t s_last_orphan; |
60 | | uint32_t s_hash_seed[4]; |
61 | | uint8_t s_def_hash_version; |
62 | | uint8_t s_jnl_backup_type; |
63 | | uint16_t s_reserved_word_pad; |
64 | | uint32_t s_default_mount_opts; |
65 | | uint32_t s_first_meta_bg; |
66 | | uint32_t s_mkfs_time; |
67 | | uint32_t s_jnl_blocks[17]; |
68 | | uint32_t s_blocks_count_hi; |
69 | | uint32_t s_r_blocks_count_hi; |
70 | | uint32_t s_free_blocks_hi; |
71 | | uint16_t s_min_extra_isize; |
72 | | uint16_t s_want_extra_isize; |
73 | | uint32_t s_flags; |
74 | | uint16_t s_raid_stride; |
75 | | uint16_t s_mmp_interval; |
76 | | uint64_t s_mmp_block; |
77 | | uint32_t s_raid_stripe_width; |
78 | | uint32_t s_reserved[162]; |
79 | | uint32_t s_checksum; |
80 | | } __attribute__((packed)); |
81 | | |
82 | | /* magic string */ |
83 | | #define EXT_SB_MAGIC "\123\357" |
84 | | /* supper block offset */ |
85 | 2.31k | #define EXT_SB_OFF 0x400 |
86 | | /* supper block offset in kB */ |
87 | | #define EXT_SB_KBOFF (EXT_SB_OFF >> 10) |
88 | | /* magic string offset within super block */ |
89 | | #define EXT_MAG_OFF 0x38 |
90 | | |
91 | | |
92 | | |
93 | | /* for s_flags */ |
94 | 82 | #define EXT2_FLAGS_TEST_FILESYS 0x0004 |
95 | | |
96 | | /* for s_feature_compat */ |
97 | 206 | #define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 |
98 | | |
99 | | /* for s_feature_ro_compat */ |
100 | 116 | #define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 |
101 | 116 | #define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 |
102 | 116 | #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 |
103 | | #define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008 |
104 | | #define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010 |
105 | | #define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020 |
106 | | #define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040 |
107 | 1.68k | #define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400 |
108 | | |
109 | | /* for s_feature_incompat */ |
110 | 53 | #define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 |
111 | 35 | #define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 |
112 | 201 | #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 |
113 | 53 | #define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 |
114 | | #define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040 /* extents support */ |
115 | 72 | #define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 |
116 | | #define EXT4_FEATURE_INCOMPAT_MMP 0x0100 |
117 | | #define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 |
118 | | |
119 | 30 | #define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ |
120 | 30 | EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ |
121 | 30 | EXT2_FEATURE_RO_COMPAT_BTREE_DIR) |
122 | 18 | #define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ |
123 | 18 | EXT2_FEATURE_INCOMPAT_META_BG) |
124 | 18 | #define EXT2_FEATURE_INCOMPAT_UNSUPPORTED ~EXT2_FEATURE_INCOMPAT_SUPP |
125 | 30 | #define EXT2_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT2_FEATURE_RO_COMPAT_SUPP |
126 | | |
127 | 86 | #define EXT3_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ |
128 | 86 | EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ |
129 | 86 | EXT2_FEATURE_RO_COMPAT_BTREE_DIR) |
130 | 35 | #define EXT3_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ |
131 | 35 | EXT3_FEATURE_INCOMPAT_RECOVER| \ |
132 | 35 | EXT2_FEATURE_INCOMPAT_META_BG) |
133 | 35 | #define EXT3_FEATURE_INCOMPAT_UNSUPPORTED ~EXT3_FEATURE_INCOMPAT_SUPP |
134 | 86 | #define EXT3_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT3_FEATURE_RO_COMPAT_SUPP |
135 | | |
136 | | /* |
137 | | * Starting in 2.6.29, ext4 can be used to support filesystems |
138 | | * without a journal. |
139 | | */ |
140 | | #define EXT4_SUPPORTS_EXT2 KERNEL_VERSION(2, 6, 29) |
141 | | |
142 | | /* |
143 | | * reads superblock and returns: |
144 | | * fc = feature_compat |
145 | | * fi = feature_incompat |
146 | | * frc = feature_ro_compat |
147 | | */ |
148 | | static struct ext2_super_block *ext_get_super( |
149 | | blkid_probe pr, uint32_t *fc, uint32_t *fi, uint32_t *frc) |
150 | 1.68k | { |
151 | 1.68k | struct ext2_super_block *es; |
152 | | |
153 | 1.68k | es = (struct ext2_super_block *) |
154 | 1.68k | blkid_probe_get_buffer(pr, EXT_SB_OFF, sizeof(struct ext2_super_block)); |
155 | 1.68k | if (!es) |
156 | 0 | return NULL; |
157 | 1.68k | if (le32_to_cpu(es->s_feature_ro_compat) & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) { |
158 | 635 | uint32_t csum = crc32c(~0, es, offsetof(struct ext2_super_block, s_checksum)); |
159 | | /* |
160 | | * A read of the superblock can race with other updates to the |
161 | | * same superblock. In the unlikely event that this occurs and |
162 | | * we see a checksum failure, re-read the superblock with |
163 | | * O_DIRECT to ensure that it's consistent. If it _still_ fails |
164 | | * then declare a checksum mismatch. |
165 | | */ |
166 | 635 | if (!blkid_probe_verify_csum(pr, csum, le32_to_cpu(es->s_checksum))) { |
167 | 625 | #ifdef O_DIRECT |
168 | 625 | if (blkid_probe_reset_buffers(pr)) |
169 | 0 | return NULL; |
170 | | |
171 | 625 | es = (struct ext2_super_block *) |
172 | 625 | blkid_probe_get_buffer_direct(pr, EXT_SB_OFF, sizeof(struct ext2_super_block)); |
173 | 625 | if (!es) |
174 | 0 | return NULL; |
175 | | |
176 | 625 | csum = crc32c(~0, es, offsetof(struct ext2_super_block, s_checksum)); |
177 | 625 | if (!blkid_probe_verify_csum(pr, csum, le32_to_cpu(es->s_checksum))) |
178 | 625 | return NULL; |
179 | | #else |
180 | | return NULL; |
181 | | #endif |
182 | 625 | } |
183 | 1.05k | } else { |
184 | | /* |
185 | | * For legacy fs without checksum, additionally verify the |
186 | | * block size to reduce false positive. Currently max allowed |
187 | | * block size is 64KiB (s_log_block_size <= 6), check for 256 |
188 | | * to be more future proof. |
189 | | */ |
190 | 1.05k | if (le32_to_cpu(es->s_log_block_size) >= 256) |
191 | 725 | return NULL; |
192 | 1.05k | } |
193 | 335 | if (fc) |
194 | 268 | *fc = le32_to_cpu(es->s_feature_compat); |
195 | 335 | if (fi) |
196 | 335 | *fi = le32_to_cpu(es->s_feature_incompat); |
197 | 335 | if (frc) |
198 | 268 | *frc = le32_to_cpu(es->s_feature_ro_compat); |
199 | | |
200 | 335 | return es; |
201 | 1.68k | } |
202 | | |
203 | | static void ext_get_info(blkid_probe pr, int ver, struct ext2_super_block *es) |
204 | 72 | { |
205 | 72 | struct blkid_chain *chn = blkid_probe_get_chain(pr); |
206 | 72 | uint32_t s_feature_incompat = le32_to_cpu(es->s_feature_incompat); |
207 | | |
208 | 72 | DBG(PROBE, ul_debug("ext2_sb.compat = %08X:%08X:%08X", |
209 | 72 | le32_to_cpu(es->s_feature_compat), |
210 | 72 | s_feature_incompat, |
211 | 72 | le32_to_cpu(es->s_feature_ro_compat))); |
212 | | |
213 | 72 | if (*es->s_volume_name != '\0') |
214 | 36 | blkid_probe_set_label(pr, (unsigned char *) es->s_volume_name, |
215 | 36 | sizeof(es->s_volume_name)); |
216 | 72 | blkid_probe_set_uuid(pr, es->s_uuid); |
217 | | |
218 | 72 | if (le32_to_cpu(es->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL) |
219 | 40 | blkid_probe_set_uuid_as(pr, es->s_journal_uuid, "EXT_JOURNAL"); |
220 | | |
221 | 72 | if (ver != 2 && (chn->flags & BLKID_SUBLKS_SECTYPE) && |
222 | 0 | ((s_feature_incompat & EXT2_FEATURE_INCOMPAT_UNSUPPORTED) == 0)) |
223 | 0 | blkid_probe_set_value(pr, "SEC_TYPE", |
224 | 0 | (unsigned char *) "ext2", |
225 | 0 | sizeof("ext2")); |
226 | | |
227 | 72 | blkid_probe_sprintf_version(pr, "%u.%u", |
228 | 72 | le32_to_cpu(es->s_rev_level), |
229 | 72 | le16_to_cpu(es->s_minor_rev_level)); |
230 | | |
231 | 72 | uint32_t block_size = 0; |
232 | 72 | if (le32_to_cpu(es->s_log_block_size) < 32){ |
233 | 60 | block_size = 1024U << le32_to_cpu(es->s_log_block_size); |
234 | 60 | blkid_probe_set_fsblocksize(pr, block_size); |
235 | 60 | blkid_probe_set_block_size(pr, block_size); |
236 | 60 | } |
237 | | |
238 | 72 | uint64_t fslastblock = le32_to_cpu(es->s_blocks_count) | |
239 | 72 | ((s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) ? |
240 | 61 | (uint64_t) le32_to_cpu(es->s_blocks_count_hi) << 32 : 0); |
241 | 72 | blkid_probe_set_fslastblock(pr, fslastblock); |
242 | | |
243 | | /* The total number of blocks is taken without subtraction of overhead |
244 | | * (journal, metadata). The ext4 has non-trivial overhead calculation |
245 | | * viz. ext4_calculate_overhead(). Therefore, the FSSIZE would show number |
246 | | * slightly higher than the real value (for example, calculated via |
247 | | * statfs()). |
248 | | */ |
249 | 72 | uint64_t fssize = (uint64_t) block_size * le32_to_cpu(es->s_blocks_count); |
250 | 72 | blkid_probe_set_fssize(pr, fssize); |
251 | 72 | } |
252 | | |
253 | | |
254 | | static int probe_jbd(blkid_probe pr, |
255 | | const struct blkid_idmag *mag __attribute__((__unused__))) |
256 | 337 | { |
257 | 337 | struct ext2_super_block *es; |
258 | 337 | uint32_t fi; |
259 | | |
260 | 337 | es = ext_get_super(pr, NULL, &fi, NULL); |
261 | 337 | if (!es) |
262 | 270 | return errno ? -errno : 1; |
263 | 67 | if (!(fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) |
264 | 49 | return 1; |
265 | | |
266 | 18 | ext_get_info(pr, 2, es); |
267 | 18 | blkid_probe_set_uuid_as(pr, es->s_uuid, "LOGUUID"); |
268 | | |
269 | 18 | return 0; |
270 | 67 | } |
271 | | |
272 | | static int probe_ext2(blkid_probe pr, |
273 | | const struct blkid_idmag *mag __attribute__((__unused__))) |
274 | 337 | { |
275 | 337 | struct ext2_super_block *es; |
276 | 337 | uint32_t fc, frc, fi; |
277 | | |
278 | 337 | es = ext_get_super(pr, &fc, &fi, &frc); |
279 | 337 | if (!es) |
280 | 270 | return errno ? -errno : 1; |
281 | | |
282 | | /* Distinguish between ext3 and ext2 */ |
283 | 67 | if (fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL) |
284 | 37 | return 1; |
285 | | |
286 | | /* Any features which ext2 doesn't understand */ |
287 | 30 | if ((frc & EXT2_FEATURE_RO_COMPAT_UNSUPPORTED) || |
288 | 18 | (fi & EXT2_FEATURE_INCOMPAT_UNSUPPORTED)) |
289 | 21 | return 1; |
290 | | |
291 | 9 | ext_get_info(pr, 2, es); |
292 | 9 | return 0; |
293 | 30 | } |
294 | | |
295 | | static int probe_ext3(blkid_probe pr, |
296 | | const struct blkid_idmag *mag __attribute__((__unused__))) |
297 | 337 | { |
298 | 337 | struct ext2_super_block *es; |
299 | 337 | uint32_t fc, frc, fi; |
300 | | |
301 | 337 | es = ext_get_super(pr, &fc, &fi, &frc); |
302 | 337 | if (!es) |
303 | 270 | return errno ? -errno : 1; |
304 | | |
305 | | /* ext3 requires journal */ |
306 | 67 | if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) |
307 | 30 | return 1; |
308 | | |
309 | | /* Any features which ext3 doesn't understand */ |
310 | 37 | if ((frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) || |
311 | 13 | (fi & EXT3_FEATURE_INCOMPAT_UNSUPPORTED)) |
312 | 34 | return 1; |
313 | | |
314 | 3 | ext_get_info(pr, 3, es); |
315 | 3 | return 0; |
316 | 37 | } |
317 | | |
318 | | |
319 | | static int probe_ext4dev(blkid_probe pr, |
320 | | const struct blkid_idmag *mag __attribute__((__unused__))) |
321 | 337 | { |
322 | 337 | struct ext2_super_block *es; |
323 | 337 | uint32_t fc, frc, fi; |
324 | | |
325 | 337 | es = ext_get_super(pr, &fc, &fi, &frc); |
326 | 337 | if (!es) |
327 | 270 | return errno ? -errno : 1; |
328 | | |
329 | | /* Distinguish from jbd */ |
330 | 67 | if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) |
331 | 18 | return 1; |
332 | | |
333 | 49 | if (!(le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS)) |
334 | 27 | return 1; |
335 | | |
336 | 22 | ext_get_info(pr, 4, es); |
337 | 22 | return 0; |
338 | 49 | } |
339 | | |
340 | | static int probe_ext4(blkid_probe pr, |
341 | | const struct blkid_idmag *mag __attribute__((__unused__))) |
342 | 337 | { |
343 | 337 | struct ext2_super_block *es; |
344 | 337 | uint32_t fc, frc, fi; |
345 | | |
346 | 337 | es = ext_get_super(pr, &fc, &fi, &frc); |
347 | 337 | if (!es) |
348 | 270 | return errno ? -errno : 1; |
349 | | |
350 | | /* Distinguish from jbd */ |
351 | 67 | if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) |
352 | 18 | return 1; |
353 | | |
354 | | /* Ext4 has at least one feature which ext3 doesn't understand */ |
355 | 49 | if (!(frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) && |
356 | 22 | !(fi & EXT3_FEATURE_INCOMPAT_UNSUPPORTED)) |
357 | 16 | return 1; |
358 | | |
359 | | /* |
360 | | * If the filesystem is a OK for use by in-development |
361 | | * filesystem code, and ext4dev is supported or ext4 is not |
362 | | * supported, then don't call ourselves ext4, so we can redo |
363 | | * the detection and mark the filesystem as ext4dev. |
364 | | * |
365 | | * If the filesystem is marked as in use by production |
366 | | * filesystem, then it can only be used by ext4 and NOT by |
367 | | * ext4dev. |
368 | | */ |
369 | 33 | if (le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) |
370 | 13 | return 1; |
371 | | |
372 | 20 | ext_get_info(pr, 4, es); |
373 | 20 | return 0; |
374 | 33 | } |
375 | | |
376 | | #define BLKID_EXT_MAGICS \ |
377 | | { \ |
378 | | { \ |
379 | | .magic = EXT_SB_MAGIC, \ |
380 | | .len = sizeof(EXT_SB_MAGIC) - 1, \ |
381 | | .kboff = EXT_SB_KBOFF, \ |
382 | | .sboff = EXT_MAG_OFF \ |
383 | | }, \ |
384 | | { NULL } \ |
385 | | } |
386 | | |
387 | | const struct blkid_idinfo jbd_idinfo = |
388 | | { |
389 | | .name = "jbd", |
390 | | .usage = BLKID_USAGE_OTHER, |
391 | | .probefunc = probe_jbd, |
392 | | .magics = BLKID_EXT_MAGICS |
393 | | }; |
394 | | |
395 | | const struct blkid_idinfo ext2_idinfo = |
396 | | { |
397 | | .name = "ext2", |
398 | | .usage = BLKID_USAGE_FILESYSTEM, |
399 | | .probefunc = probe_ext2, |
400 | | .magics = BLKID_EXT_MAGICS |
401 | | }; |
402 | | |
403 | | const struct blkid_idinfo ext3_idinfo = |
404 | | { |
405 | | .name = "ext3", |
406 | | .usage = BLKID_USAGE_FILESYSTEM, |
407 | | .probefunc = probe_ext3, |
408 | | .magics = BLKID_EXT_MAGICS |
409 | | }; |
410 | | |
411 | | const struct blkid_idinfo ext4_idinfo = |
412 | | { |
413 | | .name = "ext4", |
414 | | .usage = BLKID_USAGE_FILESYSTEM, |
415 | | .probefunc = probe_ext4, |
416 | | .magics = BLKID_EXT_MAGICS |
417 | | }; |
418 | | |
419 | | const struct blkid_idinfo ext4dev_idinfo = |
420 | | { |
421 | | .name = "ext4dev", |
422 | | .usage = BLKID_USAGE_FILESYSTEM, |
423 | | .probefunc = probe_ext4dev, |
424 | | .magics = BLKID_EXT_MAGICS |
425 | | }; |
426 | | |