blob: eff96a066b2dbbf1aa3ef9ddffcc719a7b7be82d [file] [log] [blame]
bigbiff bigbiffe60683a2013-02-22 20:55:50 -05001/*
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 "linux_version.h"
22#include "superblocks.h"
23
24struct 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[163];
79} __attribute__((packed));
80
81/* magic string */
82#define EXT_SB_MAGIC "\123\357"
83/* supper block offset */
84#define EXT_SB_OFF 0x400
85/* supper block offset in kB */
86#define EXT_SB_KBOFF (EXT_SB_OFF >> 10)
87/* magic string offset within super block */
88#define EXT_MAG_OFF 0x38
89
90
91
92/* for s_flags */
93#define EXT2_FLAGS_TEST_FILESYS 0x0004
94
95/* for s_feature_compat */
96#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004
97
98/* for s_feature_ro_compat */
99#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
100#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
101#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004
102#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008
103#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010
104#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020
105#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040
106
107/* for s_feature_incompat */
108#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
109#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004
110#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008
111#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
112#define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040 /* extents support */
113#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080
114#define EXT4_FEATURE_INCOMPAT_MMP 0x0100
115#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200
116
117#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
118 EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
119 EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
120#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \
121 EXT2_FEATURE_INCOMPAT_META_BG)
122#define EXT2_FEATURE_INCOMPAT_UNSUPPORTED ~EXT2_FEATURE_INCOMPAT_SUPP
123#define EXT2_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT2_FEATURE_RO_COMPAT_SUPP
124
125#define EXT3_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
126 EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
127 EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
128#define EXT3_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \
129 EXT3_FEATURE_INCOMPAT_RECOVER| \
130 EXT2_FEATURE_INCOMPAT_META_BG)
131#define EXT3_FEATURE_INCOMPAT_UNSUPPORTED ~EXT3_FEATURE_INCOMPAT_SUPP
132#define EXT3_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT3_FEATURE_RO_COMPAT_SUPP
133
134/*
135 * Check to see if a filesystem is in /proc/filesystems.
136 * Returns 1 if found, 0 if not
137 */
138static int fs_proc_check(const char *fs_name)
139{
140 FILE *f;
141 char buf[80], *cp, *t;
142
143 f = fopen("/proc/filesystems", "r");
144 if (!f)
145 return 0;
146 while (!feof(f)) {
147 if (!fgets(buf, sizeof(buf), f))
148 break;
149 cp = buf;
150 if (!isspace(*cp)) {
151 while (*cp && !isspace(*cp))
152 cp++;
153 }
154 while (*cp && isspace(*cp))
155 cp++;
156 if ((t = strchr(cp, '\n')) != NULL)
157 *t = 0;
158 if ((t = strchr(cp, '\t')) != NULL)
159 *t = 0;
160 if ((t = strchr(cp, ' ')) != NULL)
161 *t = 0;
162 if (!strcmp(fs_name, cp)) {
163 fclose(f);
164 return 1;
165 }
166 }
167 fclose(f);
168 return (0);
169}
170
171/*
172 * Check to see if a filesystem is available as a module
173 * Returns 1 if found, 0 if not
174 */
175static int check_for_modules(const char *fs_name)
176{
177#ifdef __linux__
178 struct utsname uts;
179 FILE *f;
180 char buf[1024], *cp;
181 int namesz;
182
183 if (uname(&uts))
184 return 0;
185 snprintf(buf, sizeof(buf), "/lib/modules/%s/modules.dep", uts.release);
186
187 f = fopen(buf, "r");
188 if (!f)
189 return 0;
190
191 namesz = strlen(fs_name);
192
193 while (!feof(f)) {
194 if (!fgets(buf, sizeof(buf), f))
195 break;
196 if ((cp = strchr(buf, ':')) != NULL)
197 *cp = 0;
198 else
199 continue;
200 if ((cp = strrchr(buf, '/')) == NULL)
201 continue;
202 cp++;
203
204 if (!strncmp(cp, fs_name, namesz) &&
205 (!strcmp(cp + namesz, ".ko") ||
206 !strcmp(cp + namesz, ".ko.gz"))) {
207 fclose(f);
208 return 1;
209 }
210 }
211 fclose(f);
212#endif /* __linux__ */
213 return 0;
214}
215
216/*
217 * Starting in 2.6.29, ext4 can be used to support filesystems
218 * without a journal.
219 */
220#define EXT4_SUPPORTS_EXT2 KERNEL_VERSION(2, 6, 29)
221
222static int system_supports_ext2(void)
223{
224 static time_t last_check = 0;
225 static int ret = -1;
226 time_t now = time(0);
227
228 if (ret != -1 || (now - last_check) < 5)
229 return ret;
230 last_check = now;
231 ret = (fs_proc_check("ext2") || check_for_modules("ext2"));
232 return ret;
233}
234
235static int system_supports_ext4(void)
236{
237 static time_t last_check = 0;
238 static int ret = -1;
239 time_t now = time(0);
240
241 if (ret != -1 || (now - last_check) < 5)
242 return ret;
243 last_check = now;
244 ret = (fs_proc_check("ext4") || check_for_modules("ext4"));
245 return ret;
246}
247
248static int system_supports_ext4dev(void)
249{
250 static time_t last_check = 0;
251 static int ret = -1;
252 time_t now = time(0);
253
254 if (ret != -1 || (now - last_check) < 5)
255 return ret;
256 last_check = now;
257 ret = (fs_proc_check("ext4dev") || check_for_modules("ext4dev"));
258 return ret;
259}
260
261static int system_supports_ext4_ext2(void)
262{
263#ifdef __linux__
264 return get_linux_version() >= EXT4_SUPPORTS_EXT2;
265#else
266 return 0;
267#endif
268}
269/*
270 * reads superblock and returns:
271 * fc = feature_compat
272 * fi = feature_incompat
273 * frc = feature_ro_compat
274 */
275static struct ext2_super_block *ext_get_super(
276 blkid_probe pr, uint32_t *fc, uint32_t *fi, uint32_t *frc)
277{
278 struct ext2_super_block *es;
279
280 es = (struct ext2_super_block *)
281 blkid_probe_get_buffer(pr, EXT_SB_OFF, 0x200);
282 if (!es)
283 return NULL;
284 if (fc)
285 *fc = le32_to_cpu(es->s_feature_compat);
286 if (fi)
287 *fi = le32_to_cpu(es->s_feature_incompat);
288 if (frc)
289 *frc = le32_to_cpu(es->s_feature_ro_compat);
290
291 return es;
292}
293
294static void ext_get_info(blkid_probe pr, int ver, struct ext2_super_block *es)
295{
296 struct blkid_chain *chn = blkid_probe_get_chain(pr);
297
298 DBG(DEBUG_PROBE, printf("ext2_sb.compat = %08X:%08X:%08X\n",
299 le32_to_cpu(es->s_feature_compat),
300 le32_to_cpu(es->s_feature_incompat),
301 le32_to_cpu(es->s_feature_ro_compat)));
302
303 if (strlen(es->s_volume_name))
304 blkid_probe_set_label(pr, (unsigned char *) es->s_volume_name,
305 sizeof(es->s_volume_name));
306 blkid_probe_set_uuid(pr, es->s_uuid);
307
308 if (le32_to_cpu(es->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL)
309 blkid_probe_set_uuid_as(pr, es->s_journal_uuid, "EXT_JOURNAL");
310
311 if (ver != 2 && (chn->flags & BLKID_SUBLKS_SECTYPE) &&
312 ((le32_to_cpu(es->s_feature_incompat) & EXT2_FEATURE_INCOMPAT_UNSUPPORTED) == 0))
313 blkid_probe_set_value(pr, "SEC_TYPE",
314 (unsigned char *) "ext2",
315 sizeof("ext2"));
316
317 blkid_probe_sprintf_version(pr, "%u.%u",
318 le32_to_cpu(es->s_rev_level),
319 le16_to_cpu(es->s_minor_rev_level));
320}
321
322
323static int probe_jbd(blkid_probe pr,
324 const struct blkid_idmag *mag __attribute__((__unused__)))
325{
326 struct ext2_super_block *es;
327 uint32_t fi;
328
329 es = ext_get_super(pr, NULL, &fi, NULL);
330 if (!es)
331 return -BLKID_ERR_PARAM;
332 if (!(fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV))
333 return -BLKID_ERR_PARAM;
334
335 ext_get_info(pr, 2, es);
336 return 0;
337}
338
339static int probe_ext2(blkid_probe pr,
340 const struct blkid_idmag *mag __attribute__((__unused__)))
341{
342 struct ext2_super_block *es;
343 uint32_t fc, frc, fi;
344
345 es = ext_get_super(pr, &fc, &fi, &frc);
346 if (!es)
347 return -BLKID_ERR_PARAM;
348
349 /* Distinguish between ext3 and ext2 */
350 if (fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL)
351 return -BLKID_ERR_PARAM;
352
353 /* Any features which ext2 doesn't understand */
354 if ((frc & EXT2_FEATURE_RO_COMPAT_UNSUPPORTED) ||
355 (fi & EXT2_FEATURE_INCOMPAT_UNSUPPORTED))
356 return -BLKID_ERR_PARAM;
357
358 /*
359 * If ext2 is not present, but ext4 or ext4dev are, then
360 * disclaim we are ext2
361 */
362 if (!system_supports_ext2() &&
363 (system_supports_ext4() || system_supports_ext4dev()) &&
364 system_supports_ext4_ext2())
365 return -BLKID_ERR_PARAM;
366
367 ext_get_info(pr, 2, es);
368 return 0;
369}
370
371static int probe_ext3(blkid_probe pr,
372 const struct blkid_idmag *mag __attribute__((__unused__)))
373{
374 struct ext2_super_block *es;
375 uint32_t fc, frc, fi;
376
377 es = ext_get_super(pr, &fc, &fi, &frc);
378 if (!es)
379 return -BLKID_ERR_PARAM;
380
381 /* ext3 requires journal */
382 if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
383 return -BLKID_ERR_PARAM;
384
385 /* Any features which ext3 doesn't understand */
386 if ((frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) ||
387 (fi & EXT3_FEATURE_INCOMPAT_UNSUPPORTED))
388 return -BLKID_ERR_PARAM;
389
390 ext_get_info(pr, 3, es);
391 return 0;
392}
393
394
395static int probe_ext4dev(blkid_probe pr,
396 const struct blkid_idmag *mag __attribute__((__unused__)))
397{
398 struct ext2_super_block *es;
399 uint32_t fc, frc, fi;
400
401 es = ext_get_super(pr, &fc, &fi, &frc);
402 if (!es)
403 return -BLKID_ERR_PARAM;
404
405 /* Distinguish from jbd */
406 if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
407 return -BLKID_ERR_PARAM;
408
409 /*
410 * If the filesystem does not have a journal and ext2 and ext4
411 * is not present, then force this to be detected as an
412 * ext4dev filesystem.
413 */
414 if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
415 !system_supports_ext2() && !system_supports_ext4() &&
416 system_supports_ext4dev() &&
417 system_supports_ext4_ext2())
418 goto force_ext4dev;
419
420 /*
421 * If the filesystem is marked as OK for use by in-development
422 * filesystem code, but ext4dev is not supported, and ext4 is,
423 * then don't call ourselves ext4dev, since we should be
424 * detected as ext4 in that case.
425 *
426 * If the filesystem is marked as in use by production
427 * filesystem, then it can only be used by ext4 and NOT by
428 * ext4dev, so always disclaim we are ext4dev in that case.
429 */
430 if (le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) {
431 if (!system_supports_ext4dev() && system_supports_ext4())
432 return -BLKID_ERR_PARAM;
433 } else
434 return -BLKID_ERR_PARAM;
435
436force_ext4dev:
437 ext_get_info(pr, 4, es);
438 return 0;
439}
440
441static int probe_ext4(blkid_probe pr,
442 const struct blkid_idmag *mag __attribute__((__unused__)))
443{
444 struct ext2_super_block *es;
445 uint32_t fc, frc, fi;
446
447 es = ext_get_super(pr, &fc, &fi, &frc);
448 if (!es)
449 return -1;
450
451 /* Distinguish from jbd */
452 if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
453 return -BLKID_ERR_PARAM;
454
455 /*
456 * If the filesystem does not have a journal and ext2 is not
457 * present, then force this to be detected as an ext2
458 * filesystem.
459 */
460 if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
461 !system_supports_ext2() && system_supports_ext4() &&
462 system_supports_ext4_ext2())
463 goto force_ext4;
464
465 /* Ext4 has at least one feature which ext3 doesn't understand */
466 if (!(frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) &&
467 !(fi & EXT3_FEATURE_INCOMPAT_UNSUPPORTED))
468 return -BLKID_ERR_PARAM;
469
470force_ext4:
471 /*
472 * If the filesystem is a OK for use by in-development
473 * filesystem code, and ext4dev is supported or ext4 is not
474 * supported, then don't call ourselves ext4, so we can redo
475 * the detection and mark the filesystem as ext4dev.
476 *
477 * If the filesystem is marked as in use by production
478 * filesystem, then it can only be used by ext4 and NOT by
479 * ext4dev.
480 */
481 if (le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) {
482 if (system_supports_ext4dev() || !system_supports_ext4())
483 return -BLKID_ERR_PARAM;
484 }
485
486 ext_get_info(pr, 4, es);
487 return 0;
488}
489
490#define BLKID_EXT_MAGICS \
491 { \
492 { \
493 .magic = EXT_SB_MAGIC, \
494 .len = sizeof(EXT_SB_MAGIC) - 1, \
495 .kboff = EXT_SB_KBOFF, \
496 .sboff = EXT_MAG_OFF \
497 }, \
498 { NULL } \
499 }
500
501const struct blkid_idinfo jbd_idinfo =
502{
503 .name = "jbd",
504 .usage = BLKID_USAGE_OTHER,
505 .probefunc = probe_jbd,
506 .magics = BLKID_EXT_MAGICS
507};
508
509const struct blkid_idinfo ext2_idinfo =
510{
511 .name = "ext2",
512 .usage = BLKID_USAGE_FILESYSTEM,
513 .probefunc = probe_ext2,
514 .magics = BLKID_EXT_MAGICS
515};
516
517const struct blkid_idinfo ext3_idinfo =
518{
519 .name = "ext3",
520 .usage = BLKID_USAGE_FILESYSTEM,
521 .probefunc = probe_ext3,
522 .magics = BLKID_EXT_MAGICS
523};
524
525const struct blkid_idinfo ext4_idinfo =
526{
527 .name = "ext4",
528 .usage = BLKID_USAGE_FILESYSTEM,
529 .probefunc = probe_ext4,
530 .magics = BLKID_EXT_MAGICS
531};
532
533const struct blkid_idinfo ext4dev_idinfo =
534{
535 .name = "ext4dev",
536 .usage = BLKID_USAGE_FILESYSTEM,
537 .probefunc = probe_ext4dev,
538 .magics = BLKID_EXT_MAGICS
539};
540