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 #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 #define EXT2_FLAGS_TEST_FILESYS 0x0004
95
96 /* for s_feature_compat */
97 #define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004
98
99 /* for s_feature_ro_compat */
100 #define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
101 #define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
102 #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 #define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400
108
109 /* for s_feature_incompat */
110 #define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
111 #define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004
112 #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008
113 #define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
114 #define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040 /* extents support */
115 #define EXT4_FEATURE_INCOMPAT_64BIT 0x0080
116 #define EXT4_FEATURE_INCOMPAT_MMP 0x0100
117 #define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200
118
119 #define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
120 EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
121 EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
122 #define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \
123 EXT2_FEATURE_INCOMPAT_META_BG)
124 #define EXT2_FEATURE_INCOMPAT_UNSUPPORTED ~EXT2_FEATURE_INCOMPAT_SUPP
125 #define EXT2_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT2_FEATURE_RO_COMPAT_SUPP
126
127 #define EXT3_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
128 EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
129 EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
130 #define EXT3_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \
131 EXT3_FEATURE_INCOMPAT_RECOVER| \
132 EXT2_FEATURE_INCOMPAT_META_BG)
133 #define EXT3_FEATURE_INCOMPAT_UNSUPPORTED ~EXT3_FEATURE_INCOMPAT_SUPP
134 #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 {
151 struct ext2_super_block *es;
152
153 es = (struct ext2_super_block *)
154 blkid_probe_get_buffer(pr, EXT_SB_OFF, sizeof(struct ext2_super_block));
155 if (!es)
156 return NULL;
157 if (le32_to_cpu(es->s_feature_ro_compat) & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
158 uint32_t csum = crc32c(~0, es, offsetof(struct ext2_super_block, s_checksum));
159 if (!blkid_probe_verify_csum(pr, csum, le32_to_cpu(es->s_checksum)))
160 return NULL;
161 }
162 if (fc)
163 *fc = le32_to_cpu(es->s_feature_compat);
164 if (fi)
165 *fi = le32_to_cpu(es->s_feature_incompat);
166 if (frc)
167 *frc = le32_to_cpu(es->s_feature_ro_compat);
168
169 return es;
170 }
171
172 static void ext_get_info(blkid_probe pr, int ver, struct ext2_super_block *es)
173 {
174 struct blkid_chain *chn = blkid_probe_get_chain(pr);
175 uint32_t s_feature_incompat = le32_to_cpu(es->s_feature_incompat);
176
177 DBG(PROBE, ul_debug("ext2_sb.compat = %08X:%08X:%08X",
178 le32_to_cpu(es->s_feature_compat),
179 s_feature_incompat,
180 le32_to_cpu(es->s_feature_ro_compat)));
181
182 if (*es->s_volume_name != '\0')
183 blkid_probe_set_label(pr, (unsigned char *) es->s_volume_name,
184 sizeof(es->s_volume_name));
185 blkid_probe_set_uuid(pr, es->s_uuid);
186
187 if (le32_to_cpu(es->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL)
188 blkid_probe_set_uuid_as(pr, es->s_journal_uuid, "EXT_JOURNAL");
189
190 if (ver != 2 && (chn->flags & BLKID_SUBLKS_SECTYPE) &&
191 ((s_feature_incompat & EXT2_FEATURE_INCOMPAT_UNSUPPORTED) == 0))
192 blkid_probe_set_value(pr, "SEC_TYPE",
193 (unsigned char *) "ext2",
194 sizeof("ext2"));
195
196 blkid_probe_sprintf_version(pr, "%u.%u",
197 le32_to_cpu(es->s_rev_level),
198 le16_to_cpu(es->s_minor_rev_level));
199
200 uint32_t block_size = 0;
201 if (le32_to_cpu(es->s_log_block_size) < 32){
202 block_size = 1024U << le32_to_cpu(es->s_log_block_size);
203 blkid_probe_set_fsblocksize(pr, block_size);
204 blkid_probe_set_block_size(pr, block_size);
205 }
206
207 uint64_t fslastblock = le32_to_cpu(es->s_blocks_count) |
208 ((s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) ?
209 (uint64_t) le32_to_cpu(es->s_blocks_count_hi) << 32 : 0);
210 blkid_probe_set_fslastblock(pr, fslastblock);
211
212 /* The total number of blocks is taken without substraction of overhead
213 * (journal, metadata). The ext4 has non-trivial overhead calculation
214 * viz. ext4_calculate_overhead(). Thefore, the FSSIZE would show number
215 * slightly higher than the real value (for example, calculated via
216 * statfs()).
217 */
218 uint64_t fssize = (uint64_t) block_size * le32_to_cpu(es->s_blocks_count);
219 blkid_probe_set_fssize(pr, fssize);
220 }
221
222
223 static int probe_jbd(blkid_probe pr,
224 const struct blkid_idmag *mag __attribute__((__unused__)))
225 {
226 struct ext2_super_block *es;
227 uint32_t fi;
228
229 es = ext_get_super(pr, NULL, &fi, NULL);
230 if (!es)
231 return errno ? -errno : 1;
232 if (!(fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV))
233 return 1;
234
235 ext_get_info(pr, 2, es);
236 blkid_probe_set_uuid_as(pr, es->s_uuid, "LOGUUID");
237
238 return 0;
239 }
240
241 static int probe_ext2(blkid_probe pr,
242 const struct blkid_idmag *mag __attribute__((__unused__)))
243 {
244 struct ext2_super_block *es;
245 uint32_t fc, frc, fi;
246
247 es = ext_get_super(pr, &fc, &fi, &frc);
248 if (!es)
249 return errno ? -errno : 1;
250
251 /* Distinguish between ext3 and ext2 */
252 if (fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL)
253 return 1;
254
255 /* Any features which ext2 doesn't understand */
256 if ((frc & EXT2_FEATURE_RO_COMPAT_UNSUPPORTED) ||
257 (fi & EXT2_FEATURE_INCOMPAT_UNSUPPORTED))
258 return 1;
259
260 ext_get_info(pr, 2, es);
261 return 0;
262 }
263
264 static int probe_ext3(blkid_probe pr,
265 const struct blkid_idmag *mag __attribute__((__unused__)))
266 {
267 struct ext2_super_block *es;
268 uint32_t fc, frc, fi;
269
270 es = ext_get_super(pr, &fc, &fi, &frc);
271 if (!es)
272 return errno ? -errno : 1;
273
274 /* ext3 requires journal */
275 if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
276 return 1;
277
278 /* Any features which ext3 doesn't understand */
279 if ((frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) ||
280 (fi & EXT3_FEATURE_INCOMPAT_UNSUPPORTED))
281 return 1;
282
283 ext_get_info(pr, 3, es);
284 return 0;
285 }
286
287
288 static int probe_ext4dev(blkid_probe pr,
289 const struct blkid_idmag *mag __attribute__((__unused__)))
290 {
291 struct ext2_super_block *es;
292 uint32_t fc, frc, fi;
293
294 es = ext_get_super(pr, &fc, &fi, &frc);
295 if (!es)
296 return errno ? -errno : 1;
297
298 /* Distinguish from jbd */
299 if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
300 return 1;
301
302 if (!(le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS))
303 return 1;
304
305 ext_get_info(pr, 4, es);
306 return 0;
307 }
308
309 static int probe_ext4(blkid_probe pr,
310 const struct blkid_idmag *mag __attribute__((__unused__)))
311 {
312 struct ext2_super_block *es;
313 uint32_t fc, frc, fi;
314
315 es = ext_get_super(pr, &fc, &fi, &frc);
316 if (!es)
317 return errno ? -errno : 1;
318
319 /* Distinguish from jbd */
320 if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
321 return 1;
322
323 /* Ext4 has at least one feature which ext3 doesn't understand */
324 if (!(frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) &&
325 !(fi & EXT3_FEATURE_INCOMPAT_UNSUPPORTED))
326 return 1;
327
328 /*
329 * If the filesystem is a OK for use by in-development
330 * filesystem code, and ext4dev is supported or ext4 is not
331 * supported, then don't call ourselves ext4, so we can redo
332 * the detection and mark the filesystem as ext4dev.
333 *
334 * If the filesystem is marked as in use by production
335 * filesystem, then it can only be used by ext4 and NOT by
336 * ext4dev.
337 */
338 if (le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS)
339 return 1;
340
341 ext_get_info(pr, 4, es);
342 return 0;
343 }
344
345 #define BLKID_EXT_MAGICS \
346 { \
347 { \
348 .magic = EXT_SB_MAGIC, \
349 .len = sizeof(EXT_SB_MAGIC) - 1, \
350 .kboff = EXT_SB_KBOFF, \
351 .sboff = EXT_MAG_OFF \
352 }, \
353 { NULL } \
354 }
355
356 const struct blkid_idinfo jbd_idinfo =
357 {
358 .name = "jbd",
359 .usage = BLKID_USAGE_OTHER,
360 .probefunc = probe_jbd,
361 .magics = BLKID_EXT_MAGICS
362 };
363
364 const struct blkid_idinfo ext2_idinfo =
365 {
366 .name = "ext2",
367 .usage = BLKID_USAGE_FILESYSTEM,
368 .probefunc = probe_ext2,
369 .magics = BLKID_EXT_MAGICS
370 };
371
372 const struct blkid_idinfo ext3_idinfo =
373 {
374 .name = "ext3",
375 .usage = BLKID_USAGE_FILESYSTEM,
376 .probefunc = probe_ext3,
377 .magics = BLKID_EXT_MAGICS
378 };
379
380 const struct blkid_idinfo ext4_idinfo =
381 {
382 .name = "ext4",
383 .usage = BLKID_USAGE_FILESYSTEM,
384 .probefunc = probe_ext4,
385 .magics = BLKID_EXT_MAGICS
386 };
387
388 const struct blkid_idinfo ext4dev_idinfo =
389 {
390 .name = "ext4dev",
391 .usage = BLKID_USAGE_FILESYSTEM,
392 .probefunc = probe_ext4dev,
393 .magics = BLKID_EXT_MAGICS
394 };
395