block: use dynamically sized probe buffer in libblkid-tiny
[project/ubox.git] / libblkid-tiny / ext.c
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 "linux_version.h"
22 #include "superblocks.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[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  */
138 static 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  */
175 static 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
222 static 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
235 static 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
248 static 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 /*
261  * reads superblock and returns:
262  *      fc = feature_compat
263  *      fi = feature_incompat
264  *      frc = feature_ro_compat
265  */
266 static struct ext2_super_block *ext_get_super(
267                 blkid_probe pr, uint32_t *fc, uint32_t *fi, uint32_t *frc)
268 {
269         struct ext2_super_block *es;
270
271         es = (struct ext2_super_block *)
272                         blkid_probe_get_buffer(pr, EXT_SB_OFF, 0x200);
273         if (!es)
274                 return NULL;
275         if (fc)
276                 *fc = le32_to_cpu(es->s_feature_compat);
277         if (fi)
278                 *fi = le32_to_cpu(es->s_feature_incompat);
279         if (frc)
280                 *frc = le32_to_cpu(es->s_feature_ro_compat);
281
282         return es;
283 }
284
285 static void ext_get_info(blkid_probe pr, int ver, struct ext2_super_block *es)
286 {
287         //struct blkid_chain *chn = blkid_probe_get_chain(pr);
288
289         DBG(DEBUG_PROBE, printf("ext2_sb.compat = %08X:%08X:%08X\n",
290                    le32_to_cpu(es->s_feature_compat),
291                    le32_to_cpu(es->s_feature_incompat),
292                    le32_to_cpu(es->s_feature_ro_compat)));
293
294         if (strlen(es->s_volume_name))
295                 blkid_probe_set_label(pr, (unsigned char *) es->s_volume_name,
296                                         sizeof(es->s_volume_name));
297         blkid_probe_set_uuid(pr, es->s_uuid);
298
299         if (le32_to_cpu(es->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL)
300                 blkid_probe_set_uuid_as(pr, es->s_journal_uuid, "EXT_JOURNAL");
301
302 /*      if (ver != 2 && (chn->flags & BLKID_SUBLKS_SECTYPE) &&
303             ((le32_to_cpu(es->s_feature_incompat) & EXT2_FEATURE_INCOMPAT_UNSUPPORTED) == 0))
304                 blkid_probe_set_value(pr, "SEC_TYPE",
305                                 (unsigned char *) "ext2",
306                                 sizeof("ext2"));*/
307
308         blkid_probe_sprintf_version(pr, "%u.%u",
309                 le32_to_cpu(es->s_rev_level),
310                 le16_to_cpu(es->s_minor_rev_level));
311 }
312
313
314 static int probe_jbd(blkid_probe pr,
315                 const struct blkid_idmag *mag __attribute__((__unused__)))
316 {
317         struct ext2_super_block *es;
318         uint32_t fi;
319
320         es = ext_get_super(pr, NULL, &fi, NULL);
321         if (!es)
322                 return -BLKID_ERR_PARAM;
323         if (!(fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV))
324                 return -BLKID_ERR_PARAM;
325
326         ext_get_info(pr, 2, es);
327         return 0;
328 }
329
330 static int probe_ext2(blkid_probe pr,
331                 const struct blkid_idmag *mag __attribute__((__unused__)))
332 {
333         struct ext2_super_block *es;
334         uint32_t fc, frc, fi;
335
336         es = ext_get_super(pr, &fc, &fi, &frc);
337         if (!es)
338                 return -BLKID_ERR_PARAM;
339
340         /* Distinguish between ext3 and ext2 */
341         if (fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL)
342                 return -BLKID_ERR_PARAM;
343
344         /* Any features which ext2 doesn't understand */
345         if ((frc & EXT2_FEATURE_RO_COMPAT_UNSUPPORTED) ||
346             (fi  & EXT2_FEATURE_INCOMPAT_UNSUPPORTED))
347                 return -BLKID_ERR_PARAM;
348
349         /*
350          * If ext2 is not present, but ext4 or ext4dev are, then
351          * disclaim we are ext2
352          */
353         if (!system_supports_ext2() &&
354             (system_supports_ext4() || system_supports_ext4dev()) &&
355             get_linux_version() >= EXT4_SUPPORTS_EXT2)
356                 return -BLKID_ERR_PARAM;
357
358         ext_get_info(pr, 2, es);
359         return 0;
360 }
361
362 static int probe_ext3(blkid_probe pr,
363                 const struct blkid_idmag *mag __attribute__((__unused__)))
364 {
365         struct ext2_super_block *es;
366         uint32_t fc, frc, fi;
367
368         es = ext_get_super(pr, &fc, &fi, &frc);
369         if (!es)
370                 return -BLKID_ERR_PARAM;
371
372         /* ext3 requires journal */
373         if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
374                 return -BLKID_ERR_PARAM;
375
376         /* Any features which ext3 doesn't understand */
377         if ((frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) ||
378             (fi  & EXT3_FEATURE_INCOMPAT_UNSUPPORTED))
379                 return -BLKID_ERR_PARAM;
380
381         ext_get_info(pr, 3, es);
382         return 0;
383 }
384
385
386 static int probe_ext4dev(blkid_probe pr,
387                 const struct blkid_idmag *mag __attribute__((__unused__)))
388 {
389         struct ext2_super_block *es;
390         uint32_t fc, frc, fi;
391
392         es = ext_get_super(pr, &fc, &fi, &frc);
393         if (!es)
394                 return -BLKID_ERR_PARAM;
395
396         /* Distinguish from jbd */
397         if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
398                 return -BLKID_ERR_PARAM;
399
400         /*
401          * If the filesystem does not have a journal and ext2 and ext4
402          * is not present, then force this to be detected as an
403          * ext4dev filesystem.
404          */
405         if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
406             !system_supports_ext2() && !system_supports_ext4() &&
407             system_supports_ext4dev() &&
408             get_linux_version() >= EXT4_SUPPORTS_EXT2)
409                 goto force_ext4dev;
410
411         /*
412          * If the filesystem is marked as OK for use by in-development
413          * filesystem code, but ext4dev is not supported, and ext4 is,
414          * then don't call ourselves ext4dev, since we should be
415          * detected as ext4 in that case.
416          *
417          * If the filesystem is marked as in use by production
418          * filesystem, then it can only be used by ext4 and NOT by
419          * ext4dev, so always disclaim we are ext4dev in that case.
420          */
421         if (le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) {
422                 if (!system_supports_ext4dev() && system_supports_ext4())
423                         return -BLKID_ERR_PARAM;
424         } else
425                 return -BLKID_ERR_PARAM;
426
427 force_ext4dev:
428         ext_get_info(pr, 4, es);
429         return 0;
430 }
431
432 static int probe_ext4(blkid_probe pr,
433                 const struct blkid_idmag *mag __attribute__((__unused__)))
434 {
435         struct ext2_super_block *es;
436         uint32_t fc, frc, fi;
437
438         es = ext_get_super(pr, &fc, &fi, &frc);
439         if (!es)
440                 return -1;
441
442         /* Distinguish from jbd */
443         if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
444                 return -BLKID_ERR_PARAM;
445
446         /*
447          * If the filesystem does not have a journal and ext2 is not
448          * present, then force this to be detected as an ext2
449          * filesystem.
450          */
451         if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
452             !system_supports_ext2() && system_supports_ext4() &&
453             get_linux_version() >= EXT4_SUPPORTS_EXT2)
454                 goto force_ext4;
455
456         /* Ext4 has at least one feature which ext3 doesn't understand */
457         if (!(frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) &&
458             !(fi  & EXT3_FEATURE_INCOMPAT_UNSUPPORTED))
459                 return -BLKID_ERR_PARAM;
460
461 force_ext4:
462         /*
463          * If the filesystem is a OK for use by in-development
464          * filesystem code, and ext4dev is supported or ext4 is not
465          * supported, then don't call ourselves ext4, so we can redo
466          * the detection and mark the filesystem as ext4dev.
467          *
468          * If the filesystem is marked as in use by production
469          * filesystem, then it can only be used by ext4 and NOT by
470          * ext4dev.
471          */
472         if (le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) {
473                 if (system_supports_ext4dev() || !system_supports_ext4())
474                         return -BLKID_ERR_PARAM;
475         }
476
477         ext_get_info(pr, 4, es);
478         return 0;
479 }
480
481 #define BLKID_EXT_MAGICS \
482         { \
483                 {        \
484                         .magic = EXT_SB_MAGIC, \
485                         .len = sizeof(EXT_SB_MAGIC) - 1, \
486                         .kboff = EXT_SB_KBOFF, \
487                         .sboff = EXT_MAG_OFF \
488                 }, \
489                 { NULL } \
490         }
491
492 const struct blkid_idinfo jbd_idinfo =
493 {
494         .name           = "jbd",
495         .usage          = BLKID_USAGE_OTHER,
496         .probefunc      = probe_jbd,
497         .magics         = BLKID_EXT_MAGICS
498 };
499
500 const struct blkid_idinfo ext2_idinfo =
501 {
502         .name           = "ext2",
503         .usage          = BLKID_USAGE_FILESYSTEM,
504         .probefunc      = probe_ext2,
505         .magics         = BLKID_EXT_MAGICS
506 };
507
508 const struct blkid_idinfo ext3_idinfo =
509 {
510         .name           = "ext3",
511         .usage          = BLKID_USAGE_FILESYSTEM,
512         .probefunc      = probe_ext3,
513         .magics         = BLKID_EXT_MAGICS
514 };
515
516 const struct blkid_idinfo ext4_idinfo =
517 {
518         .name           = "ext4",
519         .usage          = BLKID_USAGE_FILESYSTEM,
520         .probefunc      = probe_ext4,
521         .magics         = BLKID_EXT_MAGICS
522 };
523
524 const struct blkid_idinfo ext4dev_idinfo =
525 {
526         .name           = "ext4dev",
527         .usage          = BLKID_USAGE_FILESYSTEM,
528         .probefunc      = probe_ext4dev,
529         .magics         = BLKID_EXT_MAGICS
530 };
531