1 /* 2 * Copyright 1999-2001, Be Incorporated. All Rights Reserved. 3 * Copyright 2001-2020, Axel Dörfler, axeld@pinc-software.de. 4 * Copyright 2024, Haiku, Inc. All rights reserved. 5 * This file may be used under the terms of the Be Sample Code License. 6 */ 7 8 /*- 9 * SPDX-License-Identifier: BSD-4-Clause 10 * 11 * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. 12 * Copyright (C) 1994, 1995, 1997 TooLs GmbH. 13 * All rights reserved. 14 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). 15 * 16 * Redistribution and use in source and binary forms, with or without 17 * modification, are permitted provided that the following conditions 18 * are met: 19 * 1. Redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer. 21 * 2. Redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution. 24 * 3. All advertising materials mentioning features or use of this software 25 * must display the following acknowledgement: 26 * This product includes software developed by TooLs GmbH. 27 * 4. The name of TooLs GmbH may not be used to endorse or promote products 28 * derived from this software without specific prior written permission. 29 * 30 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 31 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 32 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 33 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 34 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 35 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 36 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 37 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 38 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 39 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40 */ 41 /*- 42 * Written by Paul Popelka (paulp@uts.amdahl.com) 43 * 44 * You can do anything you want with this software, just don't say you wrote 45 * it, and don't remove this notice. 46 * 47 * This software is provided "as is". 48 * 49 * The author supplies this software to be publicly redistributed on the 50 * understanding that the author is not responsible for the correct 51 * functioning of this software in any circumstances and is not liable for 52 * any damages caused by this software. 53 * 54 * October 1992 55 */ 56 57 58 #include "support.h" 59 60 #ifdef FS_SHELL 61 #include "fssh_api_wrapper.h" 62 #else // !FS_SHELL 63 #include <dirent.h> 64 #include <malloc.h> 65 66 #include <NodeMonitor.h> 67 #include <SupportDefs.h> 68 69 #include <AutoDeleter.h> 70 #include <file_systems/mime_ext_table.h> 71 #include <real_time_clock.h> 72 #include <util/AutoLock.h> 73 #endif // !FS_SHELL 74 75 #include "debug.h" 76 #include "dosfs.h" 77 #include "vcache.h" 78 79 80 extern struct iconv_functions* msdosfs_iconv; 81 82 const char* LABEL_ILLEGAL = "\"*+,./:;<=>?[\\]|"; 83 84 struct emptyDir gDirTemplate = { 85 // { ". ", /* the . entry */ 86 { 87 {'.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 88 ATTR_DIRECTORY, /* file attribute */ 89 0, /* reserved */ 90 0, {0, 0}, {0, 0}, /* create time & date */ 91 {0, 0}, /* access date */ 92 {0, 0}, /* high bits of start cluster */ 93 {210, 4}, {210, 4}, /* modify time & date */ 94 {0, 0}, /* startcluster */ 95 {0, 0, 0, 0} /* filesize */ 96 }, 97 // { ".. ", /* the .. entry */ 98 { 99 {'.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, 100 ATTR_DIRECTORY, /* file attribute */ 101 0, /* reserved */ 102 0, {0, 0}, {0, 0}, /* create time & date */ 103 {0, 0}, /* access date */ 104 {0, 0}, /* high bits of start cluster */ 105 {210, 4}, {210, 4}, /* modify time & date */ 106 {0, 0}, /* startcluster */ 107 {0, 0, 0, 0} /* filesize */ 108 }}; 109 110 111 ComponentName::ComponentName(u_int64_t flags, ucred* cred, enum nameiop nameiop, int lkflags, 112 const char* nameptr) 113 { 114 fData.cn_flags = flags; 115 fData.cn_cred = cred; 116 fData.cn_nameiop = nameiop; 117 fData.cn_lkflags = lkflags; 118 fData.cn_nameptr = strdup(nameptr); 119 fData.cn_namelen = strlen(fData.cn_nameptr); 120 } 121 122 123 ComponentName::~ComponentName() 124 { 125 free(fData.cn_nameptr); 126 } 127 128 129 struct componentname* 130 ComponentName::Data() 131 { 132 return &fData; 133 } 134 135 136 bool 137 is_filename_legal(const char* name) 138 { 139 if (name == NULL) 140 return false; 141 142 // illegal characters in a long file name 143 const char illegal[] = "\\/:*?\"<>|"; 144 145 uint32 len = strlen(name); 146 if (len <= 0) 147 return false; 148 149 // names with only an extension are not allowed 150 if (strrchr(name, '.') == name) 151 return false; 152 153 // win2unixfn will trim trailing dots and spaces from the FAT longname 154 // but it does expect there to be something there other than dots and spaces. 155 bool hasContent = false; 156 for (uint32 i = 0; i < len && hasContent == false; i++) { 157 if (name[i] != ' ' && name[i] != '.') 158 hasContent = true; 159 } 160 if (hasContent == false) 161 return false; 162 163 for (uint32 i = 0; i < len; i++) { 164 if (name[i] & 0x80) 165 continue; // belongs to an utf8 char 166 if (strchr(illegal, name[i])) 167 return false; 168 if (static_cast<unsigned char>(name[i]) < 32) 169 return false; 170 } 171 return true; 172 } 173 174 175 bool 176 is_shortname_legal(const u_char* name) 177 { 178 // if the volume is ever mounted on Windows, short name should not be the same as that 179 // of a Windows device 180 const char* device_names[] = {"CON ", "PRN ", "AUX ", "NUL ", 181 "COM0 ", "COM1 ", "COM2 ", "COM3 ", "COM4 ", "COM5 ", 182 "COM6 ", "COM7 ", "COM8 ", "COM9 ", "LPT0 ", "LPT1 ", 183 "LPT2 ", "LPT3 ", "LPT4 ", "LPT5 ", "LPT6 ", "LPT7 ", 184 "LPT8 ", "LPT9 ", NULL}; 185 186 for (uint32 i = 0; device_names[i]; i++) { 187 // only first 8 characters seem to matter 188 if (memcmp(name, device_names[i], 8) == 0) 189 return false; 190 } 191 192 return true; 193 } 194 195 196 /*! Convert a volume label from the format stored on disk into a normal string. 197 @param name A character array of length 11 or a C string of size 12. 198 @post Name is null-teriminated after the last non-space character, and converted to lower case. 199 */ 200 void 201 sanitize_label(char* name) 202 { 203 int i; 204 for (i = 10; i > 0; i--) { 205 if (name[i] != ' ') 206 break; 207 } 208 name[i + 1] = 0; 209 210 for (; i >= 0; i--) { 211 if (name[i] >= 'A' && name[i] <= 'Z') 212 name[i] += 'a' - 'A'; 213 } 214 } 215 216 217 /*! Return the volume label. 218 @param fd The device file, specified independently of volume because in 219 dosfs_identify_partition, volume->pm_dev will not be initialized. 220 @param label The pre-allocated (LABEL_CSTRING bytes) string into which the label is copied. 221 @pre The following members of volume are initialized: pm_rootdirsize, pm_BlkPerSec, 222 pm_BytesPerSec, pm_bpcluster, pm_rootdirblk, pm_bnshift. 223 @post If volume is an old DOS 3.3 era FAT volume, which will not have a label, then label is 224 set to an empty string and the function returns B_OK. For other volumes, the root 225 directory label is returned in label, if found; if not, the BPB label is returned. 226 */ 227 status_t 228 read_label(const msdosfsmount* volume, int fd, const uint8* buffer, char* label) 229 { 230 *label = '\0'; 231 232 bool fat32 = FAT32(volume); 233 uint8 check = fat32 == true ? 0x42 : 0x26; 234 uint8 offset = fat32 == true ? 0x47 : 0x2b; 235 if (buffer[check] == EXBOOTSIG && memcmp(buffer + offset, " ", LABEL_LENGTH) != 0) { 236 memcpy(label, buffer + offset, LABEL_LENGTH); 237 sanitize_label(label); 238 } else { 239 return B_OK; 240 } 241 242 // read the root directory 243 if (volume->pm_mountp != NULL) 244 volume->pm_mountp->mnt_volentry = -1; 245 int32 rootDirBytes; 246 if (fat32 == true) 247 rootDirBytes = volume->pm_bpcluster; 248 // we will only search the first cluster of the root directory, for FAT32 volumes 249 else 250 rootDirBytes = (volume->pm_rootdirsize / volume->pm_BlkPerSec) * volume->pm_BytesPerSec; 251 252 uint8* rootDirBuffer = static_cast<uint8*>(calloc(rootDirBytes, sizeof(char))); 253 daddr_t rootDirBlock = volume->pm_rootdirblk; 254 if (fat32 == true) 255 rootDirBlock = cntobn(volume, rootDirBlock); 256 off_t rootDirPos = de_bn2off(volume, rootDirBlock); 257 258 int32 bytesRead = read_pos(fd, rootDirPos, rootDirBuffer, rootDirBytes); 259 if (bytesRead != rootDirBytes) { 260 free(rootDirBuffer); 261 RETURN_ERROR(B_IO_ERROR); 262 } 263 264 // find volume label entry in the root directory (supercedes any label in the bpb) 265 // continue silently if not found. 266 uint32 direntrySize = sizeof(struct direntry); 267 uint32 rootDirEntries = rootDirBytes / direntrySize; 268 struct direntry* direntry = NULL; 269 for (uint32 i = 0; 270 i < rootDirEntries && (direntry == NULL || direntry->deName[0] != SLOT_EMPTY); ++i) { 271 direntry = reinterpret_cast<struct direntry*>(rootDirBuffer + direntrySize * i); 272 if (direntry->deName[0] != SLOT_DELETED && direntry->deAttributes != ATTR_WIN95 273 && (direntry->deAttributes & ATTR_VOLUME) != 0) { 274 memcpy(label, direntry->deName, 11); 275 sanitize_label(label); 276 PRINT("found volume label in root directory: %s at i = %" B_PRIu32 "\n", label, i); 277 if (volume->pm_mountp != NULL) 278 volume->pm_mountp->mnt_volentry = i; 279 break; 280 } 281 } 282 283 free(rootDirBuffer); 284 285 return B_OK; 286 } 287 288 289 /*! Determine whether the bootsector is from a FAT volume and, if so, the type of FAT and, if 290 FAT12 or FAT16, if it is formatted in the old DOS 3.3 format. 291 */ 292 status_t 293 check_bootsector(const uint8* bootsector, FatType& _type, bool& _dos33) 294 { 295 // check the two FAT boot signature bytes 296 if (bootsector[0x1fe] != BOOTSIG0 || bootsector[0x1ff] != BOOTSIG1) 297 return B_BAD_VALUE; 298 299 // check for indications of a non-FAT filesystem 300 if (!memcmp((uint8*) bootsector + 3, "NTFS ", 8) 301 || !memcmp((uint8*) bootsector + 3, "HPFS ", 8)) { 302 return B_BAD_VALUE; 303 } 304 305 // check the validity of a FAT BPB value to verify it is really FAT 306 uint32 bytesPerSector = read16(bootsector, 0Xb); 307 if (bytesPerSector != 512 && bytesPerSector != 1024 && bytesPerSector != 2048 308 && bytesPerSector != 4096) { 309 return B_BAD_VALUE; 310 } 311 312 // It appears to be a valid FAT volume of some kind. 313 // Determine the FAT type by counting the data clusters. 314 uint32 sectorsPerCluster = bootsector[0xd]; 315 if (sectorsPerCluster == 0) 316 return B_BAD_VALUE; 317 uint32 rootDirEntries = read16(bootsector, 0x11); 318 uint32 rootDirSectors = ((rootDirEntries * 32) + (bytesPerSector - 1)) / bytesPerSector; 319 uint32 sectorsPerFat = read16(bootsector, 0x16); 320 if (sectorsPerFat == 0) 321 sectorsPerFat = read32(bootsector, 0x24); 322 uint32 totalSectors = read16(bootsector, 0x13); 323 if (totalSectors == 0) 324 totalSectors = read32(bootsector, 0x20); 325 uint32 reservedSectors = read16(bootsector, 0xe); 326 uint32 fatCount = bootsector[0x10]; 327 uint32 dataSectors 328 = totalSectors - (reservedSectors + (fatCount * sectorsPerFat) + rootDirSectors); 329 uint32 clusterCount = dataSectors / sectorsPerCluster; 330 if (clusterCount < 4085) 331 _type = fat12; 332 else if (clusterCount < 65525) 333 _type = fat16; 334 else 335 _type = fat32; 336 337 // determine whether we are dealing with an old DOS 3.3 format 338 // volume by checking for the FAT extended boot signature 339 if (_type != fat32 && bootsector[0x26] != EXBOOTSIG) 340 _dos33 = true; 341 else 342 _dos33 = false; 343 344 return B_OK; 345 } 346 347 348 /*! Populate volume with values from the BIOS parameter block. 349 @pre Volume is already allocated and pm_fatmask has been set; if it is a temp object created 350 for use in dosfs_identify_partition, that will be indicated by a NULL volume->pm_mount. 351 @post Those members of volume that can be read directly from the BPB are initialized. 352 pm_HugeSectors will be initialized, regardless of which field the BPB stores the sector count 353 in (in contrast, pm_Sectors will not always contain an accurate count). 354 */ 355 status_t 356 parse_bpb(msdosfsmount* volume, const bootsector* bootsector, bool dos33) 357 { 358 status_t status = B_OK; 359 const universal_byte_bpb* bpb 360 = reinterpret_cast<const universal_byte_bpb*>(bootsector->bs33.bsBPB); 361 362 // first fill in the universal fields from the bpb 363 volume->pm_BytesPerSec = getushort(bpb->bpbBytesPerSec); 364 volume->pm_bpb.bpbSecPerClust = bpb->bpbSecPerClust; 365 volume->pm_ResSectors = getushort(bpb->bpbResSectors); 366 volume->pm_FATs = bpb->bpbFATs; 367 volume->pm_RootDirEnts = getushort(bpb->bpbRootDirEnts); 368 volume->pm_Sectors = getushort(bpb->bpbSectors); 369 volume->pm_Media = bpb->bpbMedia; 370 volume->pm_FATsecs = volume->pm_bpb.bpbFATsecs = getushort(bpb->bpbFATsecs); 371 volume->pm_SecPerTrack = getushort(bpb->bpbSecPerTrack); 372 volume->pm_Heads = getushort(bpb->bpbHeads); 373 374 // check the validity of some universal fields 375 if (volume->pm_BytesPerSec != 0x200) { 376 // FAT is compatible with 0x400, 0x800, and 0x1000 as well, but this driver has not 377 // been tested with those values 378 INFORM("driver does not yet support devices with > 1 block per sector\n"); 379 status = B_UNSUPPORTED; 380 } else if (volume->pm_BytesPerSec < DEV_BSIZE) { 381 INFORM("invalid bytes per sector (%d)\n", volume->pm_BytesPerSec); 382 status = B_BAD_VALUE; 383 } else if (volume->pm_bpb.bpbSecPerClust != 1 && volume->pm_bpb.bpbSecPerClust != 2 384 && volume->pm_bpb.bpbSecPerClust != 4 && volume->pm_bpb.bpbSecPerClust != 8 385 && volume->pm_bpb.bpbSecPerClust != 16 && volume->pm_bpb.bpbSecPerClust != 32 386 && volume->pm_bpb.bpbSecPerClust != 64 && volume->pm_bpb.bpbSecPerClust != 128) { 387 INFORM("invalid sectors per cluster (%" B_PRIu8 ")\n", volume->pm_bpb.bpbSecPerClust); 388 status = B_BAD_VALUE; 389 } else if (volume->pm_BytesPerSec * volume->pm_bpb.bpbSecPerClust > 0x10000) { 390 INFORM("invalid bytes per cluster (%" B_PRIu16 ")\n", 391 volume->pm_BytesPerSec * volume->pm_bpb.bpbSecPerClust); 392 status = B_BAD_VALUE; 393 } else if (volume->pm_FATs == 0 || volume->pm_FATs > 8) { 394 INFORM("unreasonable fat count (%" B_PRIu8 ")\n", volume->pm_FATs); 395 status = B_BAD_VALUE; 396 } else if (volume->pm_Media != 0xf0 && volume->pm_Media < 0xf8) { 397 INFORM("invalid media descriptor byte\n"); 398 status = B_BAD_VALUE; 399 } 400 if (status != B_OK) 401 return status; 402 403 // now become more discerning citizens 404 if (dos33 == true && (FAT12(volume) != 0 || FAT16(volume) != 0)) { 405 const struct byte_bpb33* b33 = reinterpret_cast<const byte_bpb33*>(bootsector->bs33.bsBPB); 406 if (volume->pm_Sectors == 0) { 407 INFORM("sector count missing\n"); 408 status = B_BAD_VALUE; 409 } else { 410 volume->pm_HugeSectors = volume->pm_Sectors; 411 // The driver relies on pm_HugeSectors containing the sector count, 412 // regardless of which BPB field the sector count is stored in. 413 volume->pm_HiddenSects = getushort(b33->bpbHiddenSecs); 414 volume->pm_flags |= MSDOSFS_FATMIRROR; 415 } 416 } else if (FAT12(volume) != 0 || FAT16(volume) != 0) { 417 const struct byte_bpb50* b50 = reinterpret_cast<const byte_bpb50*>(bootsector->bs50.bsBPB); 418 if (volume->pm_Sectors == 0) 419 volume->pm_HugeSectors = getulong(b50->bpbHugeSectors); 420 else 421 volume->pm_HugeSectors = volume->pm_Sectors; 422 volume->pm_HiddenSects = getulong(b50->bpbHiddenSecs); 423 volume->pm_flags |= MSDOSFS_FATMIRROR; 424 if (FAT16(volume) && volume->pm_mountp != NULL) 425 fix_zip(b50, volume); 426 } else if (FAT32(volume) != 0) { 427 const struct byte_bpb710* b710 428 = reinterpret_cast<const byte_bpb710*>(bootsector->bs710.bsBPB); 429 volume->pm_HiddenSects = getulong(b710->bpbHiddenSecs); 430 volume->pm_HugeSectors = getulong(b710->bpbHugeSectors); 431 volume->pm_FATsecs = getulong(b710->bpbBigFATsecs); 432 // overwrite the contents of the 16-bit FATsecs BPB field, which would be 0 for FAT32 433 if ((getushort(b710->bpbExtFlags) & FATMIRROR) != 0) 434 volume->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM; 435 else 436 volume->pm_flags |= MSDOSFS_FATMIRROR; 437 volume->pm_rootdirblk = getulong(b710->bpbRootClust); 438 volume->pm_fsinfo = getushort(b710->bpbFSInfo); 439 } else { 440 status = B_UNSUPPORTED; 441 } 442 443 if (status != B_OK) 444 return status; 445 446 // check the validity of some type-specific fields 447 if (FAT12(volume) || FAT16(volume)) { 448 if ((volume->pm_RootDirEnts * 32) % volume->pm_BytesPerSec != 0) { 449 INFORM("invalid number of root dir entries\n"); 450 status = B_BAD_VALUE; 451 } 452 } else if (FAT32(volume)) { 453 const struct byte_bpb710* b710 454 = reinterpret_cast<const byte_bpb710*>(bootsector->bs710.bsBPB); 455 if (getushort(b710->bpbSectors) != 0 || getushort(b710->bpbFSVers) != 0 456 || getushort(b710->bpbRootDirEnts) != 0 || getushort(b710->bpbFATsecs) != 0) { 457 INFORM("bad FAT32 filesystem\n"); 458 status = B_BAD_VALUE; 459 } 460 } 461 if ((off_t) volume->pm_HugeSectors * volume->pm_BytesPerSec < volume->pm_HugeSectors) { 462 INFORM("sectors overflow\n"); 463 status = B_BAD_VALUE; 464 } 465 if (volume->pm_mountp != NULL) { 466 if (static_cast<off_t>(volume->pm_HugeSectors) * volume->pm_BytesPerSec 467 > volume->pm_dev->si_mediasize) { 468 INFORM("sectors past end of vol: %u sectors on a %" B_PRIdOFF "-byte volume\n", 469 volume->pm_HugeSectors, volume->pm_dev->si_mediasize); 470 status = B_BAD_VALUE; 471 } 472 } 473 474 return status; 475 } 476 477 478 /*! Zip disks that were formatted at iomega have an incorrect number of sectors. 479 They say that they have 196576 sectors but they really only have 196192. 480 This check is a work-around for their brain-deadness. 481 @pre volume->pm_HugeSectors and volume->pm_dev have been initialized. 482 */ 483 void 484 fix_zip(const byte_bpb50* bpb, msdosfsmount* volume) 485 { 486 device_geometry* geo = volume->pm_dev->si_geometry; 487 488 if (geo != NULL) { 489 // the BPB values that are characteristic of these disks 490 unsigned char bogusZipData[] = {0x00, 0x02, 0x04, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 491 0xf8, 0xc0, 0x00, 0x20, 0x00, 0x40, 0x00, 0x20, 0x00, 0x00}; 492 493 if (memcmp(bpb, bogusZipData, sizeof(bogusZipData)) == 0 && volume->pm_HugeSectors == 196576 494 && (static_cast<off_t>(geo->sectors_per_track) * static_cast<off_t>(geo->cylinder_count) 495 * static_cast<off_t>(geo->head_count)) 496 == 196192) { 497 volume->pm_HugeSectors = 196192; 498 } 499 } 500 501 return; 502 } 503 504 505 /*! Collect data from the fsinfo sector, if applicable. 506 @pre devNode and the block cache are initialized. The volume is still in the process 507 of being mounted, so we have exclusive access to the sector (there is no need for a read lock). 508 */ 509 status_t 510 read_fsinfo(msdosfsmount* volume, const vnode* devNode) 511 { 512 status_t status = B_OK; 513 514 if (FAT32(volume) != 0) { 515 const uint8* buffer; 516 const struct fsinfo* fsInfo; 517 518 status = block_cache_get_etc(volume->pm_mountp->mnt_cache, volume->pm_fsinfo, 0, 1, 519 reinterpret_cast<const void**>(&buffer)); 520 if (status != B_OK) 521 RETURN_ERROR(status); 522 523 fsInfo = reinterpret_cast<const fsinfo*>(buffer); 524 if (memcmp(fsInfo->fsisig1, "RRaA", 4) == 0 && memcmp(fsInfo->fsisig2, "rrAa", 4) == 0 525 && memcmp(fsInfo->fsisig3, "\0\0\125\252", 4) == 0) { 526 volume->pm_nxtfree = getulong(fsInfo->fsinxtfree); 527 if (volume->pm_nxtfree > volume->pm_maxcluster) 528 volume->pm_nxtfree = CLUST_FIRST; 529 // fillinusemap will populate pm_freeclustercount 530 } else { 531 INFORM("fsinfo block has invalid magic number\n"); 532 status = B_BAD_VALUE; 533 volume->pm_fsinfo = 0; 534 } 535 536 block_cache_put(volume->pm_mountp->mnt_cache, volume->pm_fsinfo); 537 } 538 539 /* 540 * Finish initializing pmp->pm_nxtfree (just in case the first few 541 * sectors aren't properly reserved in the FAT). This completes 542 * the fixup for fp->fsinxtfree, and fixes up the zero-initialized 543 * value if there is no fsinfo. We will use pmp->pm_nxtfree 544 * internally even if there is no fsinfo. 545 */ 546 if (volume->pm_nxtfree < CLUST_FIRST) 547 volume->pm_nxtfree = CLUST_FIRST; 548 549 return status; 550 } 551 552 553 /*! Update the fsinfo sector, if applicable. 554 @pre the block cache is initialized. 555 */ 556 status_t 557 write_fsinfo(msdosfsmount* volume) 558 { 559 if (volume->pm_fsinfo == 0 || FAT32(volume) == false || (volume->pm_flags & MSDOSFS_FSIMOD) == 0 560 || (volume->pm_flags & MSDOSFSMNT_RONLY) != 0) { 561 return B_OK; 562 } 563 564 void* buffer = block_cache_get_writable(volume->pm_mountp->mnt_cache, volume->pm_fsinfo, -1); 565 if (buffer == NULL) 566 RETURN_ERROR(B_ERROR); 567 568 struct fsinfo* fsInfo = reinterpret_cast<struct fsinfo*>(buffer); 569 if (memcmp(fsInfo->fsisig1, "RRaA", 4) != 0 || memcmp(fsInfo->fsisig2, "rrAa", 4) != 0 570 || memcmp(fsInfo->fsisig3, "\0\0\125\252", 4) != 0) { 571 block_cache_set_dirty(volume->pm_mountp->mnt_cache, volume->pm_fsinfo, false, -1); 572 block_cache_put(volume->pm_mountp->mnt_cache, volume->pm_fsinfo); 573 RETURN_ERROR(B_ERROR); 574 } 575 576 putulong(fsInfo->fsinfree, volume->pm_freeclustercount); 577 putulong(fsInfo->fsinxtfree, volume->pm_nxtfree); 578 volume->pm_flags &= ~MSDOSFS_FSIMOD; 579 580 block_cache_put(volume->pm_mountp->mnt_cache, volume->pm_fsinfo); 581 582 return B_OK; 583 } 584 585 586 /*! Perform sanity checks on the FAT. 587 588 */ 589 status_t 590 check_fat(const msdosfsmount* volume) 591 { 592 uint8 fatBuffer[512]; 593 uint8 mirrorBuffer[512]; 594 595 // for each block 596 for (uint32 i = 0; i < volume->pm_FATsecs; ++i) { 597 // read a block from the first/active fat 598 off_t position = volume->pm_BytesPerSec 599 * (volume->pm_ResSectors + volume->pm_curfat * volume->pm_FATsecs + i); 600 ssize_t bytes_read 601 = read_pos(volume->pm_dev->si_fd, position, reinterpret_cast<void*>(fatBuffer), 0x200); 602 if (bytes_read != 0x200) 603 RETURN_ERROR(B_IO_ERROR); 604 605 if (i == 0 && fatBuffer[0] != volume->pm_Media) { 606 INFORM("media descriptor mismatch (%x != %x)\n", fatBuffer[0], volume->pm_Media); 607 return B_BAD_VALUE; 608 } 609 610 // for each mirror 611 for (uint32 j = 1; j < volume->pm_FATs; ++j) { 612 position 613 = volume->pm_BytesPerSec * (volume->pm_ResSectors + volume->pm_FATsecs * j + i); 614 bytes_read = read_pos(volume->pm_dev->si_fd, position, 615 reinterpret_cast<void*>(mirrorBuffer), 0x200); 616 if (bytes_read != 0x200) 617 RETURN_ERROR(B_IO_ERROR); 618 619 if (i == 0 && mirrorBuffer[0] != volume->pm_Media) { 620 INFORM("dosfs error: media descriptor mismatch in fat # " 621 "%" B_PRIu32 " (%" B_PRIu8 " != %" B_PRIu8 ")\n", 622 j, mirrorBuffer[0], volume->pm_Media); 623 return B_BAD_VALUE; 624 } 625 626 // checking for exact matches of fats is too 627 // restrictive; allow these to go through in 628 // case the fat is corrupted for some reason 629 if (memcmp(fatBuffer, mirrorBuffer, 0x200)) { 630 INFORM("FAT %" B_PRIu32 " doesn't match active FAT (%u) on %s.\n" 631 "Install dosfstools and use fsck.fat to inspect %s.\n", 632 j, volume->pm_curfat, volume->pm_dev->si_device, volume->pm_dev->si_device); 633 } 634 } 635 } 636 637 return B_OK; 638 } 639 640 641 /*! Traverse n fat entries. 642 E.g. for n = 1, return the cluster that is next in the chain after 'cluster' 643 If 'cluster' is the last in the chain, returns the last-cluster value. 644 */ 645 uint32 646 get_nth_fat_entry(msdosfsmount* fatVolume, uint32 cluster, uint32 n) 647 { 648 int status = 0; 649 650 ReadLocker locker(fatVolume->pm_fatlock.haikuRW); 651 652 u_long resultCluster = static_cast<u_long>(cluster); 653 654 while (n--) { 655 status = fatentry(FAT_GET, fatVolume, resultCluster, &resultCluster, 0); 656 if (status != 0) { 657 REPORT_ERROR(B_FROM_POSIX_ERROR(status)); 658 break; 659 } 660 ASSERT(IS_DATA_CLUSTER(resultCluster)); 661 } 662 663 return static_cast<uint32>(resultCluster); 664 } 665 666 667 status_t 668 init_csi(msdosfsmount* fatVolume, uint32 cluster, uint32 sector, struct csi* csi) 669 { 670 int ret; 671 if ((ret = validate_cs(fatVolume, cluster, sector)) != B_OK) 672 return ret; 673 674 csi->fatVolume = fatVolume; 675 csi->cluster = cluster; 676 csi->sector = sector; 677 678 return B_OK; 679 } 680 681 682 status_t 683 validate_cs(msdosfsmount* fatVolume, uint32 cluster, uint32 sector) 684 { 685 if (cluster == MSDOSFSROOT) { 686 if (sector >= fatVolume->pm_rootdirsize / fatVolume->pm_BlkPerSec) 687 return B_ERROR; 688 return B_OK; 689 } 690 691 if (sector >= SECTORS_PER_CLUSTER(fatVolume)) { 692 INFORM("validate_cs: invalid sector (%" B_PRIu32 ")\n", sector); 693 return B_ERROR; 694 } 695 696 if (!IS_DATA_CLUSTER(cluster)) { 697 INFORM("validate_cs: cluster %" B_PRIu32 " compared to max %lu\n", cluster, 698 fatVolume->pm_maxcluster); 699 return B_ERROR; 700 } 701 702 return B_OK; 703 } 704 705 706 /*! Return the filesystem-relative sector number that corresponds to the argument. 707 708 */ 709 off_t 710 fs_sector(struct csi* csi) 711 { 712 ASSERT(validate_cs(csi->fatVolume, csi->cluster, csi->sector) == 0); 713 714 if (csi->cluster == MSDOSFSROOT) { 715 return static_cast<off_t>(csi->fatVolume->pm_rootdirblk) / csi->fatVolume->pm_BlkPerSec 716 + csi->sector; 717 } 718 719 off_t clusterStart 720 = cntobn(csi->fatVolume, static_cast<off_t>(csi->cluster)) / csi->fatVolume->pm_BlkPerSec; 721 722 return clusterStart + csi->sector; 723 } 724 725 726 /*! Advance csi by the indicated number of sectors. 727 If it's not possible to advance this many sectors the function returns B_ERROR. 728 */ 729 status_t 730 iter_csi(struct csi* csi, int sectors) 731 { 732 if (csi->sector == 0xffff) // check if already at end of chain 733 return B_ERROR; 734 735 if (sectors < 0) 736 return B_BAD_VALUE; 737 738 if (sectors == 0) 739 return B_OK; 740 741 if (csi->cluster == MSDOSFSROOT) { 742 csi->sector += sectors; 743 if (csi->sector < csi->fatVolume->pm_rootdirsize / csi->fatVolume->pm_BlkPerSec) 744 return B_OK; 745 } else { 746 u_long secPerClust = SECTORS_PER_CLUSTER(csi->fatVolume); 747 csi->sector += sectors; 748 if (csi->sector < secPerClust) 749 return B_OK; 750 csi->cluster = get_nth_fat_entry(csi->fatVolume, csi->cluster, csi->sector / secPerClust); 751 752 if (MSDOSFSEOF(csi->fatVolume, csi->cluster)) { 753 csi->sector = 0xffff; 754 return B_ERROR; 755 } 756 757 if (vIS_DATA_CLUSTER(csi->fatVolume, csi->cluster)) { 758 csi->sector %= SECTORS_PER_CLUSTER(csi->fatVolume); 759 return B_OK; 760 } 761 } 762 763 csi->sector = 0xffff; 764 765 return B_ERROR; 766 } 767 768 769 NodePutter::NodePutter() 770 : 771 fNode(NULL) 772 { 773 } 774 775 776 NodePutter::NodePutter(vnode* node) 777 : 778 fNode(node) 779 { 780 } 781 782 783 NodePutter::~NodePutter() 784 { 785 Put(); 786 } 787 788 789 void 790 NodePutter::SetTo(vnode* node) 791 { 792 fNode = node; 793 } 794 795 796 void 797 NodePutter::Put() 798 { 799 if (fNode != NULL) { 800 fs_volume* volume = fNode->v_mount->mnt_fsvolume; 801 denode* fatNode = reinterpret_cast<denode*>(fNode->v_data); 802 ino_t ino = static_cast<ino_t>(fatNode->de_inode); 803 status_t status = put_vnode(volume, ino); 804 if (status != B_OK) 805 REPORT_ERROR(status); 806 fNode = NULL; 807 } 808 } 809 810 811 /*! Given a tentative inode number based on direntry location, set it the final inode number. 812 The final number will be different if the directory entry slot of a deleted or renamed file 813 is re-used. Also add the node to the vcache if it isn't already added. Unlike the original 814 Haiku FAT driver, each node is added to the vcache (not just those with artificial ID #s). 815 */ 816 status_t 817 assign_inode(mount* volume, ino_t* inode) 818 { 819 ino_t location = *inode; 820 status_t result = B_OK; 821 822 if (*inode == root_inode(reinterpret_cast<msdosfsmount*>(volume->mnt_data))) 823 return B_OK; 824 825 // populate finalInode with the ID of the node with this direntry location, if 826 // such a mapping exists in the vcache 827 ino_t finalInode = 0; 828 result = vcache_loc_to_vnid(volume, location, &finalInode); 829 if (result == ENOENT) { 830 // Search again, this time using the location as an ID. 831 // If an entry is found, then we know that this location is being used 832 // as an ID by some (other) node. 833 if (find_vnid_in_vcache(volume, location) == B_OK) { 834 // The location cannot be used as an ID because it is already in use. 835 // Assign an artificial ID. 836 finalInode = generate_unique_vnid(volume); 837 if ((result = add_to_vcache(volume, finalInode, location)) < 0) 838 RETURN_ERROR(result); 839 } else { 840 // otherwise we are free to use it 841 finalInode = location; 842 if ((result = add_to_vcache(volume, finalInode, location)) < 0) 843 RETURN_ERROR(result); 844 } 845 } else if (result != B_OK) { 846 RETURN_ERROR(result); 847 } 848 849 PRINT("assign_inode in: %" B_PRIdINO ", out: %" B_PRIdINO "\n", *inode, finalInode); 850 *inode = finalInode; 851 852 return B_OK; 853 } 854 855 856 status_t 857 assign_inode_and_get(mount* bsdVolume, daddr_t cluster, u_long offset, vnode** bsdNode) 858 { 859 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 860 ino_t ino = DETOI(fatVolume, cluster, offset); 861 status_t status = assign_inode(bsdVolume, &ino); 862 if (status != B_OK) 863 RETURN_ERROR(B_ENTRY_NOT_FOUND); 864 fs_volume* fsVolume = bsdVolume->mnt_fsvolume; 865 status = get_vnode(fsVolume, ino, reinterpret_cast<void**>(bsdNode)); 866 if (status != B_OK) 867 RETURN_ERROR(B_ENTRY_NOT_FOUND); 868 return B_OK; 869 } 870 871 872 /*! Convert inode into direntry location. 873 @param vnid A final inode number (possibly location-based, possibly artificial). 874 @post dirclust and diroffset specify the location of the direntry associated with vnid. If the 875 location is in the (FAT12/16) root directory, diroffset is relative to the start of the root 876 directory; otherwise it is cluster-relative. 877 */ 878 status_t 879 get_location(mount* bsdVolume, ino_t vnid, u_long* dirclust, u_long* diroffset) 880 { 881 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 882 ino_t location = -1; 883 884 vcache_vnid_to_loc(bsdVolume, vnid, &location); 885 886 if (dirclust != NULL && diroffset != NULL) { 887 // do the reverse of DETOI 888 if (static_cast<unsigned long>(location) < fatVolume->pm_RootDirEnts) { 889 // this is a regular file in a fixed root directory 890 *dirclust = MSDOSFSROOT; 891 *diroffset = location << 5; 892 } else { 893 location -= fatVolume->pm_RootDirEnts; 894 location <<= 5; 895 *dirclust = (location / fatVolume->pm_bpcluster) + 2; 896 *diroffset = location % fatVolume->pm_bpcluster; 897 } 898 } 899 900 return B_OK; 901 } 902 903 904 bool 905 node_exists(mount* bsdVolume, uint64_t inode) 906 { 907 bool constructed = false; 908 vcache_get_constructed(bsdVolume, inode, &constructed); 909 910 return constructed; 911 } 912 913 914 /*! Analagous to FreeBSD's vfs_timestamp, but returns the local time instead of GMT. 915 916 */ 917 void 918 timestamp_local(timespec* tsp) 919 { 920 bigtime_t usecs = real_time_clock_usecs(); 921 tsp->tv_sec = (usecs / 1000000LL); 922 tsp->tv_nsec = (usecs - tsp->tv_sec * 1000000LL) * 1000LL; 923 924 return; 925 } 926 927 928 void 929 local_to_GMT(const timespec* tspLocal, timespec* tspGMT) 930 { 931 *tspGMT = *tspLocal; 932 933 int32 offset = 0; 934 935 #ifdef _KERNEL_MODE 936 offset = static_cast<int32>(get_timezone_offset()); 937 #elif defined USER 938 time_t localTime; 939 time(&localTime); 940 941 struct tm localTm; 942 localtime_r(&localTime, &localTm); 943 offset = localTm.tm_gmtoff; 944 #endif 945 946 tspGMT->tv_sec -= offset; 947 948 return; 949 } 950 951 952 /*! Allocate and insert a struct buf into a buffer list. 953 @param deviceNode The node that represents the mounted device, whose v_bufobj member is to be 954 updated. 955 @param size The required size of buf::b_data. 956 @pre The msdosfsmount has been initialized and v_bufobj.bo_lock is write locked. 957 @post A new buf is inserted at the head of the bufobj SLIST that corresponds to 'size.' C-style 958 allocation so the buf can be freed in C code. 959 */ 960 status_t 961 slist_insert_buf(vnode* deviceNode, size_t size) 962 { 963 buf_list* list = NULL; 964 uint32* count = NULL; 965 msdosfsmount* fatVolume 966 = reinterpret_cast<msdosfsmount*>(deviceNode->v_rdev->si_mountpt->mnt_data); 967 968 if (size == 0) { 969 list = &deviceNode->v_bufobj.bo_emptybufs; 970 count = &deviceNode->v_bufobj.bo_empties; 971 } else if (size == fatVolume->pm_fatblocksize) { 972 list = &deviceNode->v_bufobj.bo_fatbufs; 973 count = &deviceNode->v_bufobj.bo_fatblocks; 974 } else if (size == fatVolume->pm_bpcluster) { 975 list = &deviceNode->v_bufobj.bo_clusterbufs; 976 count = &deviceNode->v_bufobj.bo_clusters; 977 } else { 978 return B_UNSUPPORTED; 979 } 980 981 buf* newBuf = reinterpret_cast<buf*>(calloc(1, sizeof(buf))); 982 if (newBuf == NULL) 983 return B_NO_MEMORY; 984 if (size != 0) { 985 newBuf->b_data = reinterpret_cast<caddr_t>(calloc(size, sizeof(char))); 986 if (newBuf->b_data == NULL) { 987 free(newBuf); 988 return B_NO_MEMORY; 989 } 990 } 991 newBuf->b_bufsize = size; 992 // The other members of newBuf will be initialized by getblkx 993 994 SLIST_INSERT_HEAD(list, newBuf, link); 995 (*count)++; 996 997 return B_OK; 998 } 999 1000 1001 status_t 1002 fill_gap_with_zeros(vnode* bsdNode, off_t pos, off_t newSize) 1003 { 1004 while (pos < newSize) { 1005 size_t size; 1006 if (newSize > pos + 1024 * 1024 * 1024) 1007 size = 1024 * 1024 * 1024; 1008 else 1009 size = newSize - pos; 1010 1011 status_t status = file_cache_write(bsdNode->v_cache, NULL, pos, NULL, &size); 1012 if (status < B_OK) 1013 return status; 1014 1015 pos += size; 1016 } 1017 1018 return B_OK; 1019 } 1020 1021 1022 /*! Sync the block cache blocks that hold the data of a directory file. 1023 1024 */ 1025 status_t 1026 sync_clusters(vnode* bsdNode) 1027 { 1028 mount* bsdVolume = bsdNode->v_mount; 1029 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data); 1030 msdosfsmount* fatVolume = fatNode->de_pmp; 1031 status_t status = B_OK; 1032 1033 ASSERT(bsdNode->v_type == VDIR); 1034 1035 u_long cluster = fatNode->de_dirclust; 1036 1037 if (cluster == MSDOSFSROOT) { 1038 status = block_cache_sync_etc(bsdVolume->mnt_cache, fatVolume->pm_rootdirblk, 1039 fatVolume->pm_rootdirsize); 1040 } else { 1041 status_t fatStatus = B_OK; 1042 while ((IS_DATA_CLUSTER(cluster)) && status == B_OK && fatStatus == B_OK) { 1043 status = block_cache_sync_etc(bsdVolume->mnt_cache, de_cn2bn(fatVolume, cluster), 1044 BLOCKS_PER_CLUSTER(fatVolume)); 1045 fatStatus = B_FROM_POSIX_ERROR(fatentry(FAT_GET, fatVolume, cluster, &cluster, 0)); 1046 } 1047 if (fatStatus != B_OK) 1048 REPORT_ERROR(fatStatus); 1049 } 1050 1051 RETURN_ERROR(status); 1052 } 1053 1054 1055 /*! Discard the block cache blocks that hold a directory's data. Has no effect on the root 1056 directory in FAT12/16; those blocks will be discarded when the volume is unmounted. 1057 */ 1058 status_t 1059 discard_clusters(vnode* bsdNode, off_t newLength) 1060 { 1061 mount* bsdVolume = bsdNode->v_mount; 1062 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data); 1063 msdosfsmount* fatVolume = fatNode->de_pmp; 1064 status_t status = B_OK; 1065 1066 ASSERT(bsdNode->v_type == VDIR); 1067 1068 // if we arrived here from detrunc, de_StartCluster has already been reset. 1069 u_long cluster = fatNode->de_dirclust; 1070 1071 // Typically we are discarding all clusters associated with a directory. However, in 1072 // the case of an error, the driver might shrink a directory to undo an attempted expansion, 1073 // as in createde. 1074 for (uint32 skip = howmany(newLength, fatVolume->pm_bpcluster); skip > 0 && status == B_OK; 1075 skip--) { 1076 status = B_FROM_POSIX_ERROR(fatentry(FAT_GET, fatVolume, cluster, &cluster, 0)); 1077 } 1078 1079 while ((IS_DATA_CLUSTER(cluster)) && status == B_OK) { 1080 block_cache_discard(bsdVolume->mnt_cache, de_cn2bn(fatVolume, cluster), 1081 BLOCKS_PER_CLUSTER(fatVolume)); 1082 status = B_FROM_POSIX_ERROR(fatentry(FAT_GET, fatVolume, cluster, &cluster, 0)); 1083 } 1084 1085 RETURN_ERROR(status); 1086 } 1087 1088 1089 /*! For use in the FAT userlandfs module. userlandfs does not provide check_access_permissions(). 1090 Limitation: ignores permissions granted by the user's group 1091 */ 1092 status_t 1093 check_access_permissions_internal(int accessMode, mode_t mode, gid_t nodeGroupID, uid_t nodeUserID) 1094 { 1095 // get node permissions 1096 int userPermissions = (mode & S_IRWXU) >> 6; 1097 int groupPermissions = (mode & S_IRWXG) >> 3; 1098 int otherPermissions = mode & S_IRWXO; 1099 1100 // get the node permissions for this uid/gid 1101 int permissions = 0; 1102 uid_t uid = geteuid(); 1103 1104 if (uid == 0) { 1105 // user is root 1106 // root has always read/write permission, but at least one of the 1107 // X bits must be set for execute permission 1108 permissions = userPermissions | groupPermissions | otherPermissions | S_IROTH | S_IWOTH; 1109 if (S_ISDIR(mode)) 1110 permissions |= S_IXOTH; 1111 } else if (uid == nodeUserID) { 1112 // user is node owner 1113 permissions = userPermissions; 1114 } else { 1115 // user is one of the others 1116 permissions = otherPermissions; 1117 } 1118 1119 // userlandfs does not provide is_user_in_group(), so we can't check group permissions 1120 1121 return (accessMode & ~permissions) == 0 ? B_OK : B_PERMISSION_DENIED; 1122 } 1123 1124 1125 /*! Populates file mode bits only (not file type bits). 1126 1127 */ 1128 void 1129 mode_bits(const vnode* bsdNode, mode_t* mode) 1130 { 1131 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data); 1132 msdosfsmount* fatVolume = fatNode->de_pmp; 1133 1134 *mode = S_IRUSR | S_IRGRP | S_IROTH; 1135 1136 if ((fatNode->de_Attributes & ATTR_READONLY) == 0) 1137 *mode |= S_IWUSR; 1138 1139 // In FAT, there is no place to store an executable flag on disk. FreeBSD makes all FAT files 1140 // executable, but Tracker will complain if, for example, a text file is executable. 1141 // To avoid that, we go by the MIME type. 1142 if (bsdNode->v_type == VDIR 1143 || (bsdNode->v_type == VREG && bsdNode->v_mime != NULL 1144 && strcmp(bsdNode->v_mime, "application/octet-stream") == 0)) { 1145 *mode |= S_IXUSR | S_IXGRP | S_IXOTH; 1146 } 1147 1148 *mode &= (bsdNode->v_type == VDIR) ? fatVolume->pm_dirmask : fatVolume->pm_mask; 1149 1150 return; 1151 } 1152 1153 1154 /*! Set the mime type of a node; has no effect in fat_shell. 1155 @param update True if this is an update to a pre-existing mime setting. 1156 */ 1157 status_t 1158 set_mime_type(vnode* bsdNode, bool update) 1159 { 1160 #ifndef FS_SHELL 1161 mount* bsdVolume = reinterpret_cast<mount*>(bsdNode->v_mount); 1162 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data); 1163 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(fatNode->de_pmp); 1164 1165 if (bsdNode->v_type == VREG) { 1166 char unixShortname[SHORTNAME_CSTRING + 1]; 1167 // +1 for the period added by dos2unixfn 1168 dos2unixfn(fatNode->de_Name, reinterpret_cast<u_char*>(unixShortname), 0, fatVolume); 1169 1170 set_mime(&bsdNode->v_mime, unixShortname); 1171 1172 notify_attribute_changed(bsdVolume->mnt_fsvolume->id, bsdNode->v_parent, fatNode->de_inode, 1173 "BEOS:TYPE", update ? B_ATTR_CHANGED : B_ATTR_CREATED); 1174 } 1175 #endif // FS_SHELL 1176 1177 return B_OK; 1178 } 1179 1180 1181 /*! Set a user-specified code page for translating FAT short filenames in UserlandFS. 1182 The FAT filesystem assigns both a short name and a long name to each file/directory. 1183 The short names are encoded in an 8-bit or DBCS OEM code page, aka DOS code page. 1184 Short names must be unique within a directory. 1185 If the user assigns a name containing a character not in the OEM code page, then 1186 the short name will substitute an underscore character. 1187 Users only see the long name, but collisions between short entry names are more likely if 1188 the OEM code page is not suitable for the user's language. 1189 FAT FS will attempt to resolve collisions by adding a generation number to the new 1190 short filename (also invisible to the user). 1191 Many code pages are supported by libiconv, which we use in the the userlandfs module. 1192 libiconv is not available in the FS shell or the kernel; in those cases the driver defaults to 1193 an internal copy of code page 850. 1194 */ 1195 status_t 1196 iconv_init(msdosfsmount* fatVolume, const char* oemPreference) 1197 { 1198 fatVolume->pm_u2w = NULL; 1199 fatVolume->pm_w2u = NULL; 1200 fatVolume->pm_u2d = NULL; 1201 fatVolume->pm_d2u = NULL; 1202 1203 #ifndef USER 1204 return B_OK; 1205 #else 1206 if ((fatVolume->pm_flags & MSDOSFSMNT_KICONV) == 0 || strcmp(oemPreference, "") == 0) 1207 return B_OK; 1208 1209 msdosfs_iconv = new(std::nothrow) iconv_functions; 1210 // fills in for FreeBSD's VFS_DECLARE_ICONV macro 1211 if (msdosfs_iconv == NULL) 1212 RETURN_ERROR(B_NO_MEMORY); 1213 ObjectDeleter<iconv_functions> deleter(msdosfs_iconv); 1214 1215 msdosfs_iconv->open = fat_iconv_open; 1216 msdosfs_iconv->close = fat_iconv_close; 1217 msdosfs_iconv->conv = iconv_conv; 1218 msdosfs_iconv->convchr = iconv_convchr; 1219 msdosfs_iconv->convchr_case = iconv_convchr_case; 1220 1221 PRINT("setting code page to %s\n", oemPreference); 1222 1223 const char* haiku = "UTF-8"; 1224 const char* windows = "UCS-2"; 1225 const char* dos = oemPreference; 1226 1227 status_t status = B_OK; 1228 status = B_FROM_POSIX_ERROR(msdosfs_iconv->open(windows, haiku, &fatVolume->pm_u2w)); 1229 if (status != B_OK) 1230 RETURN_ERROR(errno); 1231 1232 status = B_FROM_POSIX_ERROR(msdosfs_iconv->open(haiku, windows, &fatVolume->pm_w2u)); 1233 if (status != B_OK) 1234 RETURN_ERROR(errno); 1235 1236 status = B_FROM_POSIX_ERROR(msdosfs_iconv->open(dos, haiku, &fatVolume->pm_u2d)); 1237 if (status != B_OK) 1238 RETURN_ERROR(errno); 1239 1240 status = B_FROM_POSIX_ERROR(msdosfs_iconv->open(haiku, dos, &fatVolume->pm_d2u)); 1241 if (status != B_OK) 1242 RETURN_ERROR(errno); 1243 1244 deleter.Detach(); 1245 1246 return B_OK; 1247 #endif // USER 1248 } 1249