1/* 2 * volume_id - reads filesystem label and uuid 3 * 4 * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 */ 20 21#include "volume_id_internal.h" 22 23/* linux/msdos_fs.h says: */ 24#define FAT12_MAX 0xff4 25#define FAT16_MAX 0xfff4 26#define FAT32_MAX 0x0ffffff6 27 28#define FAT_ATTR_VOLUME_ID 0x08 29#define FAT_ATTR_DIR 0x10 30#define FAT_ATTR_LONG_NAME 0x0f 31#define FAT_ATTR_MASK 0x3f 32#define FAT_ENTRY_FREE 0xe5 33 34struct vfat_super_block { 35 uint8_t boot_jump[3]; 36 uint8_t sysid[8]; 37 uint16_t sector_size_bytes; 38 uint8_t sectors_per_cluster; 39 uint16_t reserved_sct; 40 uint8_t fats; 41 uint16_t dir_entries; 42 uint16_t sectors; 43 uint8_t media; 44 uint16_t fat_length; 45 uint16_t secs_track; 46 uint16_t heads; 47 uint32_t hidden; 48 uint32_t total_sect; 49 union { 50 struct fat_super_block { 51 uint8_t unknown[3]; 52 uint8_t serno[4]; 53 uint8_t label[11]; 54 uint8_t magic[8]; 55 uint8_t dummy2[192]; 56 uint8_t pmagic[2]; 57 } PACKED fat; 58 struct fat32_super_block { 59 uint32_t fat32_length; 60 uint16_t flags; 61 uint8_t version[2]; 62 uint32_t root_cluster; 63 uint16_t insfo_sector; 64 uint16_t backup_boot; 65 uint16_t reserved2[6]; 66 uint8_t unknown[3]; 67 uint8_t serno[4]; 68 uint8_t label[11]; 69 uint8_t magic[8]; 70 uint8_t dummy2[164]; 71 uint8_t pmagic[2]; 72 } PACKED fat32; 73 } PACKED type; 74} PACKED; 75 76struct vfat_dir_entry { 77 uint8_t name[11]; 78 uint8_t attr; 79 uint16_t time_creat; 80 uint16_t date_creat; 81 uint16_t time_acc; 82 uint16_t date_acc; 83 uint16_t cluster_high; 84 uint16_t time_write; 85 uint16_t date_write; 86 uint16_t cluster_low; 87 uint32_t size; 88} PACKED; 89 90static uint8_t *get_attr_volume_id(struct vfat_dir_entry *dir, int count) 91{ 92 for (;--count >= 0; dir++) { 93 /* end marker */ 94 if (dir->name[0] == 0x00) { 95 dbg("end of dir"); 96 break; 97 } 98 99 /* empty entry */ 100 if (dir->name[0] == FAT_ENTRY_FREE) 101 continue; 102 103 /* long name */ 104 if ((dir->attr & FAT_ATTR_MASK) == FAT_ATTR_LONG_NAME) 105 continue; 106 107 if ((dir->attr & (FAT_ATTR_VOLUME_ID | FAT_ATTR_DIR)) == FAT_ATTR_VOLUME_ID) { 108 /* labels do not have file data */ 109 if (dir->cluster_high != 0 || dir->cluster_low != 0) 110 continue; 111 112 dbg("found ATTR_VOLUME_ID id in root dir"); 113 return dir->name; 114 } 115 116 dbg("skip dir entry"); 117 } 118 119 return NULL; 120} 121 122int FAST_FUNC volume_id_probe_vfat(struct volume_id *id /*,uint64_t fat_partition_off*/) 123{ 124#define fat_partition_off ((uint64_t)0) 125 struct vfat_super_block *vs; 126 struct vfat_dir_entry *dir; 127 uint16_t sector_size_bytes; 128 uint16_t dir_entries; 129 uint32_t sect_count; 130 uint16_t reserved_sct; 131 uint32_t fat_size_sct; 132 uint32_t root_cluster; 133 uint32_t dir_size_sct; 134 uint32_t cluster_count; 135 uint64_t root_start_off; 136 uint32_t start_data_sct; 137 uint8_t *buf; 138 uint32_t buf_size; 139 uint8_t *label = NULL; 140 uint32_t next_cluster; 141 int maxloop; 142 143 dbg("probing at offset 0x%llx", (unsigned long long) fat_partition_off); 144 145 vs = volume_id_get_buffer(id, fat_partition_off, 0x200); 146 if (vs == NULL) 147 return -1; 148 149 /* believe only that's fat, don't trust the version 150 * the cluster_count will tell us 151 */ 152 if (memcmp(vs->sysid, "NTFS", 4) == 0) 153 return -1; 154 155 if (memcmp(vs->type.fat32.magic, "MSWIN", 5) == 0) 156 goto valid; 157 158 if (memcmp(vs->type.fat32.magic, "FAT32 ", 8) == 0) 159 goto valid; 160 161 if (memcmp(vs->type.fat.magic, "FAT16 ", 8) == 0) 162 goto valid; 163 164 if (memcmp(vs->type.fat.magic, "MSDOS", 5) == 0) 165 goto valid; 166 167 if (memcmp(vs->type.fat.magic, "FAT12 ", 8) == 0) 168 goto valid; 169 170 /* 171 * There are old floppies out there without a magic, so we check 172 * for well known values and guess if it's a fat volume 173 */ 174 175 /* boot jump address check */ 176 if ((vs->boot_jump[0] != 0xeb || vs->boot_jump[2] != 0x90) 177 && vs->boot_jump[0] != 0xe9 178 ) { 179 return -1; 180 } 181 182 /* heads check */ 183 if (vs->heads == 0) 184 return -1; 185 186 /* cluster size check */ 187 if (vs->sectors_per_cluster == 0 188 || (vs->sectors_per_cluster & (vs->sectors_per_cluster-1)) 189 ) { 190 return -1; 191 } 192 193 /* media check */ 194 if (vs->media < 0xf8 && vs->media != 0xf0) 195 return -1; 196 197 /* fat count*/ 198 if (vs->fats != 2) 199 return -1; 200 201 valid: 202 /* sector size check */ 203 sector_size_bytes = le16_to_cpu(vs->sector_size_bytes); 204 if (sector_size_bytes != 0x200 && sector_size_bytes != 0x400 205 && sector_size_bytes != 0x800 && sector_size_bytes != 0x1000 206 ) { 207 return -1; 208 } 209 210 dbg("sector_size_bytes 0x%x", sector_size_bytes); 211 dbg("sectors_per_cluster 0x%x", vs->sectors_per_cluster); 212 213 reserved_sct = le16_to_cpu(vs->reserved_sct); 214 dbg("reserved_sct 0x%x", reserved_sct); 215 216 sect_count = le16_to_cpu(vs->sectors); 217 if (sect_count == 0) 218 sect_count = le32_to_cpu(vs->total_sect); 219 dbg("sect_count 0x%x", sect_count); 220 221 fat_size_sct = le16_to_cpu(vs->fat_length); 222 if (fat_size_sct == 0) 223 fat_size_sct = le32_to_cpu(vs->type.fat32.fat32_length); 224 fat_size_sct *= vs->fats; 225 dbg("fat_size_sct 0x%x", fat_size_sct); 226 227 dir_entries = le16_to_cpu(vs->dir_entries); 228 dir_size_sct = ((dir_entries * sizeof(struct vfat_dir_entry)) + 229 (sector_size_bytes-1)) / sector_size_bytes; 230 dbg("dir_size_sct 0x%x", dir_size_sct); 231 232 cluster_count = sect_count - (reserved_sct + fat_size_sct + dir_size_sct); 233 cluster_count /= vs->sectors_per_cluster; 234 dbg("cluster_count 0x%x", cluster_count); 235 236// if (cluster_count < FAT12_MAX) { 237// strcpy(id->type_version, "FAT12"); 238// } else if (cluster_count < FAT16_MAX) { 239// strcpy(id->type_version, "FAT16"); 240// } else { 241// strcpy(id->type_version, "FAT32"); 242// goto fat32; 243// } 244 if (cluster_count >= FAT16_MAX) 245 goto fat32; 246 247 /* the label may be an attribute in the root directory */ 248 root_start_off = (reserved_sct + fat_size_sct) * sector_size_bytes; 249 dbg("root dir start 0x%llx", (unsigned long long) root_start_off); 250 dbg("expected entries 0x%x", dir_entries); 251 252 buf_size = dir_entries * sizeof(struct vfat_dir_entry); 253 buf = volume_id_get_buffer(id, fat_partition_off + root_start_off, buf_size); 254 if (buf == NULL) 255 goto ret; 256 257 label = get_attr_volume_id((struct vfat_dir_entry*) buf, dir_entries); 258 259 vs = volume_id_get_buffer(id, fat_partition_off, 0x200); 260 if (vs == NULL) 261 return -1; 262 263 if (label != NULL && memcmp(label, "NO NAME ", 11) != 0) { 264// volume_id_set_label_raw(id, label, 11); 265 volume_id_set_label_string(id, label, 11); 266 } else if (memcmp(vs->type.fat.label, "NO NAME ", 11) != 0) { 267// volume_id_set_label_raw(id, vs->type.fat.label, 11); 268 volume_id_set_label_string(id, vs->type.fat.label, 11); 269 } 270 volume_id_set_uuid(id, vs->type.fat.serno, UUID_DOS); 271 goto ret; 272 273 fat32: 274 /* FAT32 root dir is a cluster chain like any other directory */ 275 buf_size = vs->sectors_per_cluster * sector_size_bytes; 276 root_cluster = le32_to_cpu(vs->type.fat32.root_cluster); 277 start_data_sct = reserved_sct + fat_size_sct; 278 279 next_cluster = root_cluster; 280 maxloop = 100; 281 while (--maxloop) { 282 uint64_t next_off_sct; 283 uint64_t next_off; 284 uint64_t fat_entry_off; 285 int count; 286 287 dbg("next_cluster 0x%x", (unsigned)next_cluster); 288 next_off_sct = (uint64_t)(next_cluster - 2) * vs->sectors_per_cluster; 289 next_off = (start_data_sct + next_off_sct) * sector_size_bytes; 290 dbg("cluster offset 0x%llx", (unsigned long long) next_off); 291 292 /* get cluster */ 293 buf = volume_id_get_buffer(id, fat_partition_off + next_off, buf_size); 294 if (buf == NULL) 295 goto ret; 296 297 dir = (struct vfat_dir_entry*) buf; 298 count = buf_size / sizeof(struct vfat_dir_entry); 299 dbg("expected entries 0x%x", count); 300 301 label = get_attr_volume_id(dir, count); 302 if (label) 303 break; 304 305 /* get FAT entry */ 306 fat_entry_off = (reserved_sct * sector_size_bytes) + (next_cluster * sizeof(uint32_t)); 307 dbg("fat_entry_off 0x%llx", (unsigned long long)fat_entry_off); 308 buf = volume_id_get_buffer(id, fat_partition_off + fat_entry_off, buf_size); 309 if (buf == NULL) 310 goto ret; 311 312 /* set next cluster */ 313 next_cluster = le32_to_cpu(*(uint32_t*)buf) & 0x0fffffff; 314 if (next_cluster < 2 || next_cluster > FAT32_MAX) 315 break; 316 } 317 if (maxloop == 0) 318 dbg("reached maximum follow count of root cluster chain, give up"); 319 320 vs = volume_id_get_buffer(id, fat_partition_off, 0x200); 321 if (vs == NULL) 322 return -1; 323 324 if (label != NULL && memcmp(label, "NO NAME ", 11) != 0) { 325// volume_id_set_label_raw(id, label, 11); 326 volume_id_set_label_string(id, label, 11); 327 } else if (memcmp(vs->type.fat32.label, "NO NAME ", 11) != 0) { 328// volume_id_set_label_raw(id, vs->type.fat32.label, 11); 329 volume_id_set_label_string(id, vs->type.fat32.label, 11); 330 } 331 volume_id_set_uuid(id, vs->type.fat32.serno, UUID_DOS); 332 333 ret: 334// volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); 335// id->type = "vfat"; 336 337 return 0; 338} 339