/src/util-linux/libblkid/src/superblocks/vfat.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 1999 by Andries Brouwer |
3 | | * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o |
4 | | * Copyright (C) 2001 by Andreas Dilger |
5 | | * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> |
6 | | * Copyright (C) 2008 Karel Zak <kzak@redhat.com> |
7 | | * |
8 | | * This file may be redistributed under the terms of the |
9 | | * GNU Lesser General Public License. |
10 | | */ |
11 | | #include <stdio.h> |
12 | | #include <stdlib.h> |
13 | | #include <unistd.h> |
14 | | #include <string.h> |
15 | | #include <errno.h> |
16 | | #include <ctype.h> |
17 | | #include <stdint.h> |
18 | | #include <inttypes.h> |
19 | | |
20 | | #include "pt-mbr.h" |
21 | | #include "superblocks.h" |
22 | | |
23 | | /* Yucky misaligned values */ |
24 | | struct vfat_super_block { |
25 | | /* 00*/ unsigned char vs_ignored[3]; |
26 | | /* 03*/ unsigned char vs_sysid[8]; |
27 | | /* 0b*/ unsigned char vs_sector_size[2]; |
28 | | /* 0d*/ uint8_t vs_cluster_size; |
29 | | /* 0e*/ uint16_t vs_reserved; |
30 | | /* 10*/ uint8_t vs_fats; |
31 | | /* 11*/ unsigned char vs_dir_entries[2]; |
32 | | /* 13*/ unsigned char vs_sectors[2]; |
33 | | /* 15*/ unsigned char vs_media; |
34 | | /* 16*/ uint16_t vs_fat_length; |
35 | | /* 18*/ uint16_t vs_secs_track; |
36 | | /* 1a*/ uint16_t vs_heads; |
37 | | /* 1c*/ uint32_t vs_hidden; |
38 | | /* 20*/ uint32_t vs_total_sect; |
39 | | /* 24*/ uint32_t vs_fat32_length; |
40 | | /* 28*/ uint16_t vs_flags; |
41 | | /* 2a*/ uint8_t vs_version[2]; |
42 | | /* 2c*/ uint32_t vs_root_cluster; |
43 | | /* 30*/ uint16_t vs_fsinfo_sector; |
44 | | /* 32*/ uint16_t vs_backup_boot; |
45 | | /* 34*/ uint16_t vs_reserved2[6]; |
46 | | /* 40*/ unsigned char vs_drive_number; |
47 | | /* 41*/ unsigned char vs_boot_flags; |
48 | | /* 42*/ unsigned char vs_ext_boot_sign; /* 0x28 - without vs_label/vs_magic; 0x29 - with */ |
49 | | /* 43*/ unsigned char vs_serno[4]; |
50 | | /* 47*/ unsigned char vs_label[11]; |
51 | | /* 52*/ unsigned char vs_magic[8]; |
52 | | /* 5a*/ unsigned char vs_dummy2[0x1fe - 0x5a]; |
53 | | /*1fe*/ unsigned char vs_pmagic[2]; |
54 | | } __attribute__((packed)); |
55 | | |
56 | | /* Yucky misaligned values */ |
57 | | struct msdos_super_block { |
58 | | /* DOS 2.0 BPB */ |
59 | | /* 00*/ unsigned char ms_ignored[3]; |
60 | | /* 03*/ unsigned char ms_sysid[8]; |
61 | | /* 0b*/ unsigned char ms_sector_size[2]; |
62 | | /* 0d*/ uint8_t ms_cluster_size; |
63 | | /* 0e*/ uint16_t ms_reserved; |
64 | | /* 10*/ uint8_t ms_fats; |
65 | | /* 11*/ unsigned char ms_dir_entries[2]; |
66 | | /* 13*/ unsigned char ms_sectors[2]; /* =0 iff V3 or later */ |
67 | | /* 15*/ unsigned char ms_media; |
68 | | /* 16*/ uint16_t ms_fat_length; /* Sectors per FAT */ |
69 | | /* DOS 3.0 BPB */ |
70 | | /* 18*/ uint16_t ms_secs_track; |
71 | | /* 1a*/ uint16_t ms_heads; |
72 | | /* 1c*/ uint32_t ms_hidden; |
73 | | /* DOS 3.31 BPB */ |
74 | | /* 20*/ uint32_t ms_total_sect; /* iff ms_sectors == 0 */ |
75 | | /* DOS 3.4 EBPB */ |
76 | | /* 24*/ unsigned char ms_drive_number; |
77 | | /* 25*/ unsigned char ms_boot_flags; |
78 | | /* 26*/ unsigned char ms_ext_boot_sign; /* 0x28 - DOS 3.4 EBPB; 0x29 - DOS 4.0 EBPB */ |
79 | | /* 27*/ unsigned char ms_serno[4]; |
80 | | /* DOS 4.0 EBPB */ |
81 | | /* 2b*/ unsigned char ms_label[11]; |
82 | | /* 36*/ unsigned char ms_magic[8]; |
83 | | /* padding */ |
84 | | /* 3e*/ unsigned char ms_dummy2[0x1fe - 0x3e]; |
85 | | /*1fe*/ unsigned char ms_pmagic[2]; |
86 | | } __attribute__((packed)); |
87 | | |
88 | | struct vfat_dir_entry { |
89 | | uint8_t name[11]; |
90 | | uint8_t attr; |
91 | | uint16_t time_creat; |
92 | | uint16_t date_creat; |
93 | | uint16_t time_acc; |
94 | | uint16_t date_acc; |
95 | | uint16_t cluster_high; |
96 | | uint16_t time_write; |
97 | | uint16_t date_write; |
98 | | uint16_t cluster_low; |
99 | | uint32_t size; |
100 | | } __attribute__((packed)); |
101 | | |
102 | | struct fat32_fsinfo { |
103 | | uint8_t signature1[4]; |
104 | | uint32_t reserved1[120]; |
105 | | uint8_t signature2[4]; |
106 | | uint32_t free_clusters; |
107 | | uint32_t next_cluster; |
108 | | uint32_t reserved2[4]; |
109 | | } __attribute__((packed)); |
110 | | |
111 | | /* maximum number of clusters */ |
112 | 197 | #define FAT12_MAX 0xFF4 |
113 | 146 | #define FAT16_MAX 0xFFF4 |
114 | 565 | #define FAT32_MAX 0x0FFFFFF6 |
115 | | |
116 | 5.73k | #define FAT_ATTR_VOLUME_ID 0x08 |
117 | 2.86k | #define FAT_ATTR_DIR 0x10 |
118 | 3.20k | #define FAT_ATTR_LONG_NAME 0x0f |
119 | 3.20k | #define FAT_ATTR_MASK 0x3f |
120 | 98.2k | #define FAT_ENTRY_FREE 0xe5 |
121 | | |
122 | | static const char *no_name = "NO NAME "; |
123 | | |
124 | | #define unaligned_le16(x) \ |
125 | 2.88k | (((unsigned char *) x)[0] + (((unsigned char *) x)[1] << 8)) |
126 | | |
127 | | /* |
128 | | * Look for LABEL (name) in the FAT root directory. |
129 | | */ |
130 | | static int search_fat_label(blkid_probe pr, uint64_t offset, uint32_t entries, unsigned char out[11]) |
131 | 6.17k | { |
132 | 6.17k | const struct vfat_dir_entry *ent, *dir = NULL; |
133 | 6.17k | uint32_t i; |
134 | | |
135 | 6.17k | DBG(LOWPROBE, ul_debug("\tlook for label in root-dir " |
136 | 6.17k | "(entries: %"PRIu32", offset: %"PRIu64")", entries, offset)); |
137 | | |
138 | 6.17k | if (!blkid_probe_is_tiny(pr)) { |
139 | | /* large disk, read whole root directory */ |
140 | 6.17k | dir = (struct vfat_dir_entry *) |
141 | 6.17k | blkid_probe_get_buffer(pr, |
142 | 6.17k | offset, |
143 | 6.17k | (uint64_t) entries * |
144 | 6.17k | sizeof(struct vfat_dir_entry)); |
145 | 6.17k | if (!dir) |
146 | 2.56k | return 0; |
147 | 6.17k | } |
148 | | |
149 | 101k | for (i = 0; i < entries; i++) { |
150 | | /* |
151 | | * The root directory could be relatively large (4-16kB). |
152 | | * Fortunately, the LABEL is usually the first entry in the |
153 | | * directory. On tiny disks we call read() per entry. |
154 | | */ |
155 | 101k | if (!dir) |
156 | 0 | ent = (struct vfat_dir_entry *) |
157 | 0 | blkid_probe_get_buffer(pr, |
158 | 0 | (uint64_t) offset + (i * |
159 | 0 | sizeof(struct vfat_dir_entry)), |
160 | 0 | sizeof(struct vfat_dir_entry)); |
161 | 101k | else |
162 | 101k | ent = &dir[i]; |
163 | | |
164 | 101k | if (!ent || ent->name[0] == 0x00) |
165 | 3.24k | break; |
166 | | |
167 | 98.2k | if ((ent->name[0] == FAT_ENTRY_FREE) || |
168 | 97.5k | (ent->cluster_high != 0 || ent->cluster_low != 0) || |
169 | 3.20k | ((ent->attr & FAT_ATTR_MASK) == FAT_ATTR_LONG_NAME)) |
170 | 95.4k | continue; |
171 | | |
172 | 2.86k | if ((ent->attr & (FAT_ATTR_VOLUME_ID | FAT_ATTR_DIR)) == |
173 | 2.86k | FAT_ATTR_VOLUME_ID) { |
174 | 22 | DBG(LOWPROBE, ul_debug("\tfound fs LABEL at entry %d", i)); |
175 | 22 | memcpy(out, ent->name, 11); |
176 | 22 | if (out[0] == 0x05) |
177 | 3 | out[0] = 0xE5; |
178 | 22 | return 1; |
179 | 22 | } |
180 | 2.86k | } |
181 | 3.58k | return 0; |
182 | 3.60k | } |
183 | | |
184 | | static int fat_valid_superblock(blkid_probe pr, |
185 | | const struct blkid_idmag *mag, |
186 | | const struct msdos_super_block *ms, |
187 | | const struct vfat_super_block *vs, |
188 | | uint32_t *cluster_count, uint32_t *fat_size, |
189 | | uint32_t *sect_count) |
190 | 3.06k | { |
191 | 3.06k | uint16_t sector_size, dir_entries, reserved; |
192 | 3.06k | uint32_t __sect_count, __fat_size, dir_size, __cluster_count, fat_length; |
193 | 3.06k | uint32_t max_count; |
194 | | |
195 | | /* extra check for FATs without magic strings */ |
196 | 3.06k | if (mag->len <= 2) { |
197 | | /* Old floppies have a valid MBR signature */ |
198 | 1.53k | if (ms->ms_pmagic[0] != 0x55 || ms->ms_pmagic[1] != 0xAA) |
199 | 165 | return 0; |
200 | | |
201 | | /* |
202 | | * OS/2 and apparently DFSee will place a FAT12/16-like |
203 | | * pseudo-superblock in the first 512 bytes of non-FAT |
204 | | * filesystems --- at least JFS and HPFS, and possibly others. |
205 | | * So we explicitly check for those filesystems at the |
206 | | * FAT12/16 filesystem magic field identifier, and if they are |
207 | | * present, we rule this out as a FAT filesystem, despite the |
208 | | * FAT-like pseudo-header. |
209 | | */ |
210 | 1.37k | if ((memcmp(ms->ms_magic, "JFS ", 8) == 0) || |
211 | 1.33k | (memcmp(ms->ms_magic, "HPFS ", 8) == 0)) { |
212 | 103 | DBG(LOWPROBE, ul_debug("\tJFS/HPFS detected")); |
213 | 103 | return 0; |
214 | 103 | } |
215 | 1.37k | } |
216 | | |
217 | | /* fat counts(Linux kernel expects at least 1 FAT table) */ |
218 | 2.79k | if (!ms->ms_fats) |
219 | 700 | return 0; |
220 | 2.09k | if (!ms->ms_reserved) |
221 | 97 | return 0; |
222 | 1.99k | if (!(0xf8 <= ms->ms_media || ms->ms_media == 0xf0)) |
223 | 790 | return 0; |
224 | 1.20k | if (!is_power_of_2(ms->ms_cluster_size)) |
225 | 305 | return 0; |
226 | | |
227 | 900 | sector_size = unaligned_le16(&ms->ms_sector_size); |
228 | 900 | if (!is_power_of_2(sector_size) || |
229 | 743 | sector_size < 512 || sector_size > 4096) |
230 | 194 | return 0; |
231 | | |
232 | 706 | dir_entries = unaligned_le16(&ms->ms_dir_entries); |
233 | 706 | reserved = le16_to_cpu(ms->ms_reserved); |
234 | 706 | __sect_count = unaligned_le16(&ms->ms_sectors); |
235 | | |
236 | 706 | if (__sect_count == 0) |
237 | 374 | __sect_count = le32_to_cpu(ms->ms_total_sect); |
238 | | |
239 | 706 | fat_length = le16_to_cpu(ms->ms_fat_length); |
240 | 706 | if (fat_length == 0) |
241 | 589 | fat_length = le32_to_cpu(vs->vs_fat32_length); |
242 | | |
243 | 706 | __fat_size = fat_length * ms->ms_fats; |
244 | 706 | dir_size = ((dir_entries * sizeof(struct vfat_dir_entry)) + |
245 | 706 | (sector_size-1)) / sector_size; |
246 | | |
247 | 706 | __cluster_count = (__sect_count - (reserved + __fat_size + dir_size)) / |
248 | 706 | ms->ms_cluster_size; |
249 | 706 | if (!ms->ms_fat_length && vs->vs_fat32_length) |
250 | 565 | max_count = FAT32_MAX; |
251 | 141 | else |
252 | 141 | max_count = __cluster_count > FAT12_MAX ? FAT16_MAX : FAT12_MAX; |
253 | | |
254 | 706 | if (__cluster_count > max_count) |
255 | 99 | return 0; |
256 | | |
257 | 607 | if (fat_size) |
258 | 540 | *fat_size = __fat_size; |
259 | 607 | if (cluster_count) |
260 | 540 | *cluster_count = __cluster_count; |
261 | 607 | if (sect_count) |
262 | 540 | *sect_count = __sect_count; |
263 | | |
264 | 607 | if (blkid_probe_is_bitlocker(pr)) |
265 | 13 | return 0; |
266 | | |
267 | 594 | return 1; /* valid */ |
268 | 607 | } |
269 | | |
270 | | /* function prototype to avoid warnings (duplicate in partitions/dos.c) */ |
271 | | extern int blkid_probe_is_vfat(blkid_probe pr); |
272 | | |
273 | | /* |
274 | | * This function is used by MBR partition table parser to avoid |
275 | | * misinterpretation of FAT filesystem. |
276 | | */ |
277 | | int blkid_probe_is_vfat(blkid_probe pr) |
278 | 132 | { |
279 | 132 | const struct vfat_super_block *vs; |
280 | 132 | const struct msdos_super_block *ms; |
281 | 132 | const struct blkid_idmag *mag = NULL; |
282 | 132 | int rc; |
283 | | |
284 | 132 | rc = blkid_probe_get_idmag(pr, &vfat_idinfo, NULL, &mag); |
285 | 132 | if (rc < 0) |
286 | 0 | return rc; /* error */ |
287 | 132 | if (rc != BLKID_PROBE_OK || !mag) |
288 | 0 | return 0; |
289 | | |
290 | 132 | ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block); |
291 | 132 | if (!ms) |
292 | 0 | return errno ? -errno : 0; |
293 | 132 | vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block); |
294 | 132 | if (!vs) |
295 | 0 | return errno ? -errno : 0; |
296 | | |
297 | 132 | return fat_valid_superblock(pr, mag, ms, vs, NULL, NULL, NULL); |
298 | 132 | } |
299 | | |
300 | | /* FAT label extraction from the root directory taken from Kay |
301 | | * Sievers's volume_id library */ |
302 | | static int probe_vfat(blkid_probe pr, const struct blkid_idmag *mag) |
303 | 2.92k | { |
304 | 2.92k | const struct vfat_super_block *vs; |
305 | 2.92k | const struct msdos_super_block *ms; |
306 | 2.92k | const unsigned char *vol_label = NULL; |
307 | 2.92k | const unsigned char *boot_label = NULL; |
308 | 2.92k | const unsigned char *vol_serno = NULL; |
309 | 2.92k | unsigned char vol_label_buf[11]; |
310 | 2.92k | uint16_t sector_size = 0, reserved; |
311 | 2.92k | uint32_t cluster_count, fat_size, sect_count; |
312 | 2.92k | const char *version = NULL; |
313 | | |
314 | 2.92k | ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block); |
315 | 2.92k | if (!ms) |
316 | 0 | return errno ? -errno : 1; |
317 | | |
318 | 2.92k | vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block); |
319 | 2.92k | if (!vs) |
320 | 0 | return errno ? -errno : 1; |
321 | | |
322 | 2.92k | if (!fat_valid_superblock(pr, mag, ms, vs, &cluster_count, &fat_size, |
323 | 2.92k | §_count)) |
324 | 2.39k | return 1; |
325 | | |
326 | 535 | sector_size = unaligned_le16(&ms->ms_sector_size); |
327 | 535 | reserved = le16_to_cpu(ms->ms_reserved); |
328 | | |
329 | 535 | if (ms->ms_fat_length) { |
330 | | /* the label may be an attribute in the root directory */ |
331 | 38 | uint32_t root_start = (reserved + fat_size) * sector_size; |
332 | 38 | uint32_t root_dir_entries = unaligned_le16(&vs->vs_dir_entries); |
333 | | |
334 | 38 | if (search_fat_label(pr, root_start, root_dir_entries, vol_label_buf)) |
335 | 2 | vol_label = vol_label_buf; |
336 | | |
337 | 38 | if (ms->ms_ext_boot_sign == 0x29) |
338 | 2 | boot_label = ms->ms_label; |
339 | | |
340 | 38 | if (ms->ms_ext_boot_sign == 0x28 || ms->ms_ext_boot_sign == 0x29) |
341 | 3 | vol_serno = ms->ms_serno; |
342 | | |
343 | 38 | blkid_probe_set_value(pr, "SEC_TYPE", (unsigned char *) "msdos", |
344 | 38 | sizeof("msdos")); |
345 | | |
346 | 38 | if (cluster_count < FAT12_MAX) |
347 | 15 | version = "FAT12"; |
348 | 23 | else if (cluster_count < FAT16_MAX) |
349 | 23 | version = "FAT16"; |
350 | | |
351 | 497 | } else if (vs->vs_fat32_length) { |
352 | 491 | const unsigned char *buf; |
353 | 491 | uint16_t fsinfo_sect; |
354 | 491 | int maxloop = 100; |
355 | | |
356 | | /* Search the FAT32 root dir for the label attribute */ |
357 | 491 | uint32_t buf_size = vs->vs_cluster_size * sector_size; |
358 | 491 | uint32_t start_data_sect = reserved + fat_size; |
359 | 491 | uint32_t entries = ((uint64_t) le32_to_cpu(vs->vs_fat32_length) |
360 | 491 | * sector_size) / sizeof(uint32_t); |
361 | 491 | uint32_t next = le32_to_cpu(vs->vs_root_cluster); |
362 | | |
363 | 6.49k | while (next && next < entries && --maxloop) { |
364 | 6.13k | uint32_t next_sect_off; |
365 | 6.13k | uint64_t next_off, fat_entry_off; |
366 | 6.13k | int count; |
367 | | |
368 | 6.13k | next_sect_off = (next - 2) * vs->vs_cluster_size; |
369 | 6.13k | next_off = (uint64_t)(start_data_sect + next_sect_off) * |
370 | 6.13k | sector_size; |
371 | | |
372 | 6.13k | count = buf_size / sizeof(struct vfat_dir_entry); |
373 | | |
374 | 6.13k | if (search_fat_label(pr, next_off, count, vol_label_buf)) { |
375 | 20 | vol_label = vol_label_buf; |
376 | 20 | break; |
377 | 20 | } |
378 | | |
379 | | /* get FAT entry */ |
380 | 6.11k | fat_entry_off = ((uint64_t) reserved * sector_size) + |
381 | 6.11k | (next * sizeof(uint32_t)); |
382 | 6.11k | buf = blkid_probe_get_buffer(pr, fat_entry_off, buf_size); |
383 | 6.11k | if (buf == NULL) |
384 | 115 | break; |
385 | | |
386 | | /* set next cluster */ |
387 | 6.00k | next = le32_to_cpu(*((uint32_t *) buf)) & 0x0fffffff; |
388 | 6.00k | } |
389 | | |
390 | 491 | version = "FAT32"; |
391 | | |
392 | 491 | if (vs->vs_ext_boot_sign == 0x29) |
393 | 14 | boot_label = vs->vs_label; |
394 | | |
395 | 491 | vol_serno = vs->vs_serno; |
396 | | |
397 | | /* |
398 | | * FAT32 should have a valid signature in the fsinfo block, |
399 | | * but also allow all bytes set to '\0', because some volumes |
400 | | * do not set the signature at all. |
401 | | */ |
402 | 491 | fsinfo_sect = le16_to_cpu(vs->vs_fsinfo_sector); |
403 | 491 | if (fsinfo_sect) { |
404 | 474 | struct fat32_fsinfo *fsinfo; |
405 | | |
406 | 474 | buf = blkid_probe_get_buffer(pr, |
407 | 474 | (uint64_t) fsinfo_sect * sector_size, |
408 | 474 | sizeof(struct fat32_fsinfo)); |
409 | 474 | if (buf == NULL) |
410 | 101 | return errno ? -errno : 1; |
411 | | |
412 | 373 | fsinfo = (struct fat32_fsinfo *) buf; |
413 | 373 | if (memcmp(fsinfo->signature1, "\x52\x52\x61\x41", 4) != 0 && |
414 | 370 | memcmp(fsinfo->signature1, "\x52\x52\x64\x41", 4) != 0 && |
415 | 365 | memcmp(fsinfo->signature1, "\x00\x00\x00\x00", 4) != 0) |
416 | 230 | return 1; |
417 | 143 | if (memcmp(fsinfo->signature2, "\x72\x72\x41\x61", 4) != 0 && |
418 | 142 | memcmp(fsinfo->signature2, "\x00\x00\x00\x00", 4) != 0) |
419 | 72 | return 1; |
420 | 143 | } |
421 | 491 | } |
422 | | |
423 | 132 | if (boot_label && memcmp(boot_label, no_name, 11) != 0) |
424 | 8 | blkid_probe_set_id_label(pr, "LABEL_FATBOOT", boot_label, 11); |
425 | | |
426 | 132 | if (vol_label) |
427 | 5 | blkid_probe_set_label(pr, vol_label, 11); |
428 | | |
429 | | /* We can't just print them as %04X, because they are unaligned */ |
430 | 132 | if (vol_serno) |
431 | 91 | blkid_probe_sprintf_uuid(pr, vol_serno, 4, "%02X%02X-%02X%02X", |
432 | 91 | vol_serno[3], vol_serno[2], vol_serno[1], vol_serno[0]); |
433 | 132 | if (version) |
434 | 126 | blkid_probe_set_version(pr, version); |
435 | | |
436 | 132 | blkid_probe_set_fsblocksize(pr, vs->vs_cluster_size * sector_size); |
437 | 132 | blkid_probe_set_block_size(pr, sector_size); |
438 | 132 | blkid_probe_set_fssize(pr, (uint64_t) sector_size * sect_count); |
439 | | |
440 | 132 | return 0; |
441 | 535 | } |
442 | | |
443 | | |
444 | | const struct blkid_idinfo vfat_idinfo = |
445 | | { |
446 | | .name = "vfat", |
447 | | .usage = BLKID_USAGE_FILESYSTEM, |
448 | | .probefunc = probe_vfat, |
449 | | .magics = |
450 | | { |
451 | | { .magic = "MSWIN", .len = 5, .sboff = 0x52 }, |
452 | | { .magic = "FAT32 ", .len = 8, .sboff = 0x52 }, |
453 | | { .magic = "MSDOS", .len = 5, .sboff = 0x36 }, |
454 | | { .magic = "FAT16 ", .len = 8, .sboff = 0x36 }, |
455 | | { .magic = "FAT12 ", .len = 8, .sboff = 0x36 }, |
456 | | { .magic = "FAT ", .len = 8, .sboff = 0x36 }, |
457 | | { .magic = "\353", .len = 1, }, |
458 | | { .magic = "\351", .len = 1, }, |
459 | | { .magic = "\125\252", .len = 2, .sboff = 0x1fe }, |
460 | | { NULL } |
461 | | } |
462 | | }; |
463 | | |