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 && volume->pm_BytesPerSec != 0x400 376 && volume->pm_BytesPerSec != 0x800 && volume->pm_BytesPerSec != 0x1000) { 377 INFORM("invalid bytes per sector (%d)\n", volume->pm_BytesPerSec); 378 status = B_BAD_VALUE; 379 } else if (volume->pm_BytesPerSec < DEV_BSIZE) { 380 INFORM("invalid bytes per sector (%d)\n", volume->pm_BytesPerSec); 381 status = B_BAD_VALUE; 382 } else if (volume->pm_bpb.bpbSecPerClust != 1 && volume->pm_bpb.bpbSecPerClust != 2 383 && volume->pm_bpb.bpbSecPerClust != 4 && volume->pm_bpb.bpbSecPerClust != 8 384 && volume->pm_bpb.bpbSecPerClust != 16 && volume->pm_bpb.bpbSecPerClust != 32 385 && volume->pm_bpb.bpbSecPerClust != 64 && volume->pm_bpb.bpbSecPerClust != 128) { 386 INFORM("invalid sectors per cluster (%" B_PRIu8 ")\n", volume->pm_bpb.bpbSecPerClust); 387 status = B_BAD_VALUE; 388 } else if (volume->pm_BytesPerSec * volume->pm_bpb.bpbSecPerClust > 0x10000) { 389 INFORM("invalid bytes per cluster (%" B_PRIu16 ")\n", 390 volume->pm_BytesPerSec * volume->pm_bpb.bpbSecPerClust); 391 status = B_BAD_VALUE; 392 } else if (volume->pm_FATs == 0 || volume->pm_FATs > 8) { 393 INFORM("unreasonable fat count (%" B_PRIu8 ")\n", volume->pm_FATs); 394 status = B_BAD_VALUE; 395 } else if (volume->pm_Media != 0xf0 && volume->pm_Media < 0xf8) { 396 INFORM("invalid media descriptor byte\n"); 397 status = B_BAD_VALUE; 398 } 399 if (status != B_OK) 400 return status; 401 402 // now become more discerning citizens 403 if (dos33 == true && (FAT12(volume) != 0 || FAT16(volume) != 0)) { 404 const struct byte_bpb33* b33 = reinterpret_cast<const byte_bpb33*>(bootsector->bs33.bsBPB); 405 if (volume->pm_Sectors == 0) { 406 INFORM("sector count missing\n"); 407 status = B_BAD_VALUE; 408 } else { 409 volume->pm_HugeSectors = volume->pm_Sectors; 410 // The driver relies on pm_HugeSectors containing the sector count, 411 // regardless of which BPB field the sector count is stored in. 412 volume->pm_HiddenSects = getushort(b33->bpbHiddenSecs); 413 volume->pm_flags |= MSDOSFS_FATMIRROR; 414 } 415 } else if (FAT12(volume) != 0 || FAT16(volume) != 0) { 416 const struct byte_bpb50* b50 = reinterpret_cast<const byte_bpb50*>(bootsector->bs50.bsBPB); 417 if (volume->pm_Sectors == 0) 418 volume->pm_HugeSectors = getulong(b50->bpbHugeSectors); 419 else 420 volume->pm_HugeSectors = volume->pm_Sectors; 421 volume->pm_HiddenSects = getulong(b50->bpbHiddenSecs); 422 volume->pm_flags |= MSDOSFS_FATMIRROR; 423 if (FAT16(volume) && volume->pm_mountp != NULL) 424 fix_zip(b50, volume); 425 } else if (FAT32(volume) != 0) { 426 const struct byte_bpb710* b710 427 = reinterpret_cast<const byte_bpb710*>(bootsector->bs710.bsBPB); 428 volume->pm_HiddenSects = getulong(b710->bpbHiddenSecs); 429 volume->pm_HugeSectors = getulong(b710->bpbHugeSectors); 430 volume->pm_FATsecs = getulong(b710->bpbBigFATsecs); 431 // overwrite the contents of the 16-bit FATsecs BPB field, which would be 0 for FAT32 432 if ((getushort(b710->bpbExtFlags) & FATMIRROR) != 0) 433 volume->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM; 434 else 435 volume->pm_flags |= MSDOSFS_FATMIRROR; 436 volume->pm_rootdirblk = getulong(b710->bpbRootClust); 437 volume->pm_fsinfo = getushort(b710->bpbFSInfo); 438 } else { 439 status = B_UNSUPPORTED; 440 } 441 442 if (status != B_OK) 443 return status; 444 445 // check the validity of some type-specific fields 446 if (FAT12(volume) || FAT16(volume)) { 447 if ((volume->pm_RootDirEnts * 32) % volume->pm_BytesPerSec != 0) { 448 INFORM("invalid number of root dir entries\n"); 449 status = B_BAD_VALUE; 450 } 451 } else if (FAT32(volume)) { 452 const struct byte_bpb710* b710 453 = reinterpret_cast<const byte_bpb710*>(bootsector->bs710.bsBPB); 454 if (getushort(b710->bpbSectors) != 0 || getushort(b710->bpbFSVers) != 0 455 || getushort(b710->bpbRootDirEnts) != 0 || getushort(b710->bpbFATsecs) != 0) { 456 INFORM("bad FAT32 filesystem\n"); 457 status = B_BAD_VALUE; 458 } 459 } 460 if ((off_t) volume->pm_HugeSectors * volume->pm_BytesPerSec < volume->pm_HugeSectors) { 461 INFORM("sectors overflow\n"); 462 status = B_BAD_VALUE; 463 } 464 if (volume->pm_mountp != NULL) { 465 if (static_cast<off_t>(volume->pm_HugeSectors) * volume->pm_BytesPerSec 466 > volume->pm_dev->si_mediasize) { 467 INFORM("sectors past end of vol: %u sectors on a %" B_PRIdOFF "-byte volume\n", 468 volume->pm_HugeSectors, volume->pm_dev->si_mediasize); 469 status = B_BAD_VALUE; 470 } 471 } 472 473 return status; 474 } 475 476 477 /*! Zip disks that were formatted at iomega have an incorrect number of sectors. 478 They say that they have 196576 sectors but they really only have 196192. 479 This check is a work-around for their brain-deadness. 480 @pre volume->pm_HugeSectors and volume->pm_dev have been initialized. 481 */ 482 void 483 fix_zip(const byte_bpb50* bpb, msdosfsmount* volume) 484 { 485 device_geometry* geo = volume->pm_dev->si_geometry; 486 487 if (geo != NULL) { 488 // the BPB values that are characteristic of these disks 489 unsigned char bogusZipData[] = {0x00, 0x02, 0x04, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 490 0xf8, 0xc0, 0x00, 0x20, 0x00, 0x40, 0x00, 0x20, 0x00, 0x00}; 491 492 if (memcmp(bpb, bogusZipData, sizeof(bogusZipData)) == 0 && volume->pm_HugeSectors == 196576 493 && (static_cast<off_t>(geo->sectors_per_track) * static_cast<off_t>(geo->cylinder_count) 494 * static_cast<off_t>(geo->head_count)) 495 == 196192) { 496 volume->pm_HugeSectors = 196192; 497 } 498 } 499 500 return; 501 } 502 503 504 /*! Collect data from the fsinfo sector, if applicable. 505 @pre devNode and the block cache are initialized. The volume is still in the process 506 of being mounted, so we have exclusive access to the sector (there is no need for a read lock). 507 */ 508 status_t 509 read_fsinfo(msdosfsmount* volume, const vnode* devNode) 510 { 511 status_t status = B_OK; 512 513 if (FAT32(volume) != 0) { 514 const uint8* buffer; 515 const struct fsinfo* fsInfo; 516 517 status = block_cache_get_etc(volume->pm_mountp->mnt_cache, volume->pm_fsinfo, 0, 1, 518 reinterpret_cast<const void**>(&buffer)); 519 if (status != B_OK) 520 RETURN_ERROR(status); 521 522 fsInfo = reinterpret_cast<const fsinfo*>(buffer); 523 if (memcmp(fsInfo->fsisig1, "RRaA", 4) == 0 && memcmp(fsInfo->fsisig2, "rrAa", 4) == 0 524 && memcmp(fsInfo->fsisig3, "\0\0\125\252", 4) == 0) { 525 volume->pm_nxtfree = getulong(fsInfo->fsinxtfree); 526 if (volume->pm_nxtfree > volume->pm_maxcluster) 527 volume->pm_nxtfree = CLUST_FIRST; 528 // fillinusemap will populate pm_freeclustercount 529 } else { 530 INFORM("fsinfo block has invalid magic number\n"); 531 status = B_BAD_VALUE; 532 volume->pm_fsinfo = 0; 533 } 534 535 block_cache_put(volume->pm_mountp->mnt_cache, volume->pm_fsinfo); 536 } 537 538 /* 539 * Finish initializing pmp->pm_nxtfree (just in case the first few 540 * sectors aren't properly reserved in the FAT). This completes 541 * the fixup for fp->fsinxtfree, and fixes up the zero-initialized 542 * value if there is no fsinfo. We will use pmp->pm_nxtfree 543 * internally even if there is no fsinfo. 544 */ 545 if (volume->pm_nxtfree < CLUST_FIRST) 546 volume->pm_nxtfree = CLUST_FIRST; 547 548 return status; 549 } 550 551 552 /*! Update the fsinfo sector, if applicable. 553 @pre the block cache is initialized. 554 */ 555 status_t 556 write_fsinfo(msdosfsmount* volume) 557 { 558 if (volume->pm_fsinfo == 0 || FAT32(volume) == false || (volume->pm_flags & MSDOSFS_FSIMOD) == 0 559 || (volume->pm_flags & MSDOSFSMNT_RONLY) != 0) { 560 return B_OK; 561 } 562 563 void* buffer = block_cache_get_writable(volume->pm_mountp->mnt_cache, volume->pm_fsinfo, -1); 564 if (buffer == NULL) 565 RETURN_ERROR(B_ERROR); 566 567 struct fsinfo* fsInfo = reinterpret_cast<struct fsinfo*>(buffer); 568 if (memcmp(fsInfo->fsisig1, "RRaA", 4) != 0 || memcmp(fsInfo->fsisig2, "rrAa", 4) != 0 569 || memcmp(fsInfo->fsisig3, "\0\0\125\252", 4) != 0) { 570 block_cache_set_dirty(volume->pm_mountp->mnt_cache, volume->pm_fsinfo, false, -1); 571 block_cache_put(volume->pm_mountp->mnt_cache, volume->pm_fsinfo); 572 RETURN_ERROR(B_ERROR); 573 } 574 575 putulong(fsInfo->fsinfree, volume->pm_freeclustercount); 576 putulong(fsInfo->fsinxtfree, volume->pm_nxtfree); 577 volume->pm_flags &= ~MSDOSFS_FSIMOD; 578 579 block_cache_put(volume->pm_mountp->mnt_cache, volume->pm_fsinfo); 580 581 return B_OK; 582 } 583 584 585 /*! Perform sanity checks on the FAT. 586 587 */ 588 status_t 589 check_fat(const msdosfsmount* volume) 590 { 591 uint8 fatBuffer[512]; 592 uint8 mirrorBuffer[512]; 593 594 // for each block 595 for (uint32 i = 0; i < volume->pm_FATsecs; ++i) { 596 // read a block from the first/active fat 597 uint32 resBlocks = volume->pm_ResSectors * volume->pm_BlkPerSec; 598 off_t position = 512 * (resBlocks + volume->pm_curfat * volume->pm_FATsecs + i); 599 ssize_t bytes_read 600 = read_pos(volume->pm_dev->si_fd, position, reinterpret_cast<void*>(fatBuffer), 0x200); 601 if (bytes_read != 0x200) 602 RETURN_ERROR(B_IO_ERROR); 603 604 if (i == 0 && fatBuffer[0] != volume->pm_Media) { 605 INFORM("media descriptor mismatch (%x != %x)\n", fatBuffer[0], volume->pm_Media); 606 return B_BAD_VALUE; 607 } 608 609 // for each mirror 610 for (uint32 j = 1; j < volume->pm_FATs; ++j) { 611 position = 512 * (resBlocks + volume->pm_FATsecs * j + i); 612 bytes_read = read_pos(volume->pm_dev->si_fd, position, 613 reinterpret_cast<void*>(mirrorBuffer), 0x200); 614 if (bytes_read != 0x200) 615 RETURN_ERROR(B_IO_ERROR); 616 617 if (i == 0 && mirrorBuffer[0] != volume->pm_Media) { 618 INFORM("dosfs error: media descriptor mismatch in fat # " 619 "%" B_PRIu32 " (%" B_PRIu8 " != %" B_PRIu8 ")\n", 620 j, mirrorBuffer[0], volume->pm_Media); 621 return B_BAD_VALUE; 622 } 623 624 // checking for exact matches of fats is too 625 // restrictive; allow these to go through in 626 // case the fat is corrupted for some reason 627 if (memcmp(fatBuffer, mirrorBuffer, 0x200)) { 628 INFORM("FAT %" B_PRIu32 " doesn't match active FAT (%u) on %s.\n" 629 "Install dosfstools and use fsck.fat to inspect %s.\n", 630 j, volume->pm_curfat, volume->pm_dev->si_device, volume->pm_dev->si_device); 631 } 632 } 633 } 634 635 return B_OK; 636 } 637 638 639 /*! Traverse n fat entries. 640 E.g. for n = 1, return the cluster that is next in the chain after 'cluster' 641 If 'cluster' is the last in the chain, returns the last-cluster value. 642 */ 643 uint32 644 get_nth_fat_entry(msdosfsmount* fatVolume, uint32 cluster, uint32 n) 645 { 646 int status = 0; 647 648 ReadLocker locker(fatVolume->pm_fatlock.haikuRW); 649 650 u_long resultCluster = static_cast<u_long>(cluster); 651 652 while (n--) { 653 status = fatentry(FAT_GET, fatVolume, resultCluster, &resultCluster, 0); 654 if (status != 0) { 655 REPORT_ERROR(B_FROM_POSIX_ERROR(status)); 656 break; 657 } 658 ASSERT(IS_DATA_CLUSTER(resultCluster)); 659 } 660 661 return static_cast<uint32>(resultCluster); 662 } 663 664 665 status_t 666 init_csi(msdosfsmount* fatVolume, uint32 cluster, uint32 sector, struct csi* csi) 667 { 668 int ret; 669 if ((ret = validate_cs(fatVolume, cluster, sector)) != B_OK) 670 return ret; 671 672 csi->fatVolume = fatVolume; 673 csi->cluster = cluster; 674 csi->sector = sector; 675 676 return B_OK; 677 } 678 679 680 status_t 681 validate_cs(msdosfsmount* fatVolume, uint32 cluster, uint32 sector) 682 { 683 if (cluster == MSDOSFSROOT) { 684 if (sector >= fatVolume->pm_rootdirsize / fatVolume->pm_BlkPerSec) 685 return B_ERROR; 686 return B_OK; 687 } 688 689 if (sector >= SECTORS_PER_CLUSTER(fatVolume)) { 690 INFORM("validate_cs: invalid sector (%" B_PRIu32 ")\n", sector); 691 return B_ERROR; 692 } 693 694 if (!IS_DATA_CLUSTER(cluster)) { 695 INFORM("validate_cs: cluster %" B_PRIu32 " compared to max %lu\n", cluster, 696 fatVolume->pm_maxcluster); 697 return B_ERROR; 698 } 699 700 return B_OK; 701 } 702 703 704 /*! Return the filesystem-relative sector number that corresponds to the argument. 705 706 */ 707 off_t 708 fs_sector(struct csi* csi) 709 { 710 ASSERT(validate_cs(csi->fatVolume, csi->cluster, csi->sector) == 0); 711 712 if (csi->cluster == MSDOSFSROOT) { 713 return static_cast<off_t>(csi->fatVolume->pm_rootdirblk) / csi->fatVolume->pm_BlkPerSec 714 + csi->sector; 715 } 716 717 off_t clusterStart 718 = cntobn(csi->fatVolume, static_cast<off_t>(csi->cluster)) / csi->fatVolume->pm_BlkPerSec; 719 720 return clusterStart + csi->sector; 721 } 722 723 724 /*! Advance csi by the indicated number of sectors. 725 If it's not possible to advance this many sectors the function returns B_ERROR. 726 */ 727 status_t 728 iter_csi(struct csi* csi, int sectors) 729 { 730 if (csi->sector == 0xffff) // check if already at end of chain 731 return B_ERROR; 732 733 if (sectors < 0) 734 return B_BAD_VALUE; 735 736 if (sectors == 0) 737 return B_OK; 738 739 if (csi->cluster == MSDOSFSROOT) { 740 csi->sector += sectors; 741 if (csi->sector < csi->fatVolume->pm_rootdirsize / csi->fatVolume->pm_BlkPerSec) 742 return B_OK; 743 } else { 744 u_long secPerClust = SECTORS_PER_CLUSTER(csi->fatVolume); 745 csi->sector += sectors; 746 if (csi->sector < secPerClust) 747 return B_OK; 748 csi->cluster = get_nth_fat_entry(csi->fatVolume, csi->cluster, csi->sector / secPerClust); 749 750 if (MSDOSFSEOF(csi->fatVolume, csi->cluster)) { 751 csi->sector = 0xffff; 752 return B_ERROR; 753 } 754 755 if (vIS_DATA_CLUSTER(csi->fatVolume, csi->cluster)) { 756 csi->sector %= SECTORS_PER_CLUSTER(csi->fatVolume); 757 return B_OK; 758 } 759 } 760 761 csi->sector = 0xffff; 762 763 return B_ERROR; 764 } 765 766 767 NodePutter::NodePutter() 768 : 769 fNode(NULL) 770 { 771 } 772 773 774 NodePutter::NodePutter(vnode* node) 775 : 776 fNode(node) 777 { 778 } 779 780 781 NodePutter::~NodePutter() 782 { 783 Put(); 784 } 785 786 787 void 788 NodePutter::SetTo(vnode* node) 789 { 790 fNode = node; 791 } 792 793 794 void 795 NodePutter::Put() 796 { 797 if (fNode != NULL) { 798 fs_volume* volume = fNode->v_mount->mnt_fsvolume; 799 denode* fatNode = reinterpret_cast<denode*>(fNode->v_data); 800 ino_t ino = static_cast<ino_t>(fatNode->de_inode); 801 status_t status = put_vnode(volume, ino); 802 if (status != B_OK) 803 REPORT_ERROR(status); 804 fNode = NULL; 805 } 806 } 807 808 809 /*! Given a tentative inode number based on direntry location, set it the final inode number. 810 The final number will be different if the directory entry slot of a deleted or renamed file 811 is re-used. Also add the node to the vcache if it isn't already added. Unlike the original 812 Haiku FAT driver, each node is added to the vcache (not just those with artificial ID #s). 813 */ 814 status_t 815 assign_inode(mount* volume, ino_t* inode) 816 { 817 ino_t location = *inode; 818 status_t result = B_OK; 819 820 if (*inode == root_inode(reinterpret_cast<msdosfsmount*>(volume->mnt_data))) 821 return B_OK; 822 823 // populate finalInode with the ID of the node with this direntry location, if 824 // such a mapping exists in the vcache 825 ino_t finalInode = 0; 826 result = vcache_loc_to_vnid(volume, location, &finalInode); 827 if (result == ENOENT) { 828 // Search again, this time using the location as an ID. 829 // If an entry is found, then we know that this location is being used 830 // as an ID by some (other) node. 831 if (find_vnid_in_vcache(volume, location) == B_OK) { 832 // The location cannot be used as an ID because it is already in use. 833 // Assign an artificial ID. 834 finalInode = generate_unique_vnid(volume); 835 if ((result = add_to_vcache(volume, finalInode, location)) < 0) 836 RETURN_ERROR(result); 837 } else { 838 // otherwise we are free to use it 839 finalInode = location; 840 if ((result = add_to_vcache(volume, finalInode, location)) < 0) 841 RETURN_ERROR(result); 842 } 843 } else if (result != B_OK) { 844 RETURN_ERROR(result); 845 } 846 847 PRINT("assign_inode in: %" B_PRIdINO ", out: %" B_PRIdINO "\n", *inode, finalInode); 848 *inode = finalInode; 849 850 return B_OK; 851 } 852 853 854 status_t 855 assign_inode_and_get(mount* bsdVolume, daddr_t cluster, u_long offset, vnode** bsdNode) 856 { 857 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 858 ino_t ino = DETOI(fatVolume, cluster, offset); 859 status_t status = assign_inode(bsdVolume, &ino); 860 if (status != B_OK) 861 RETURN_ERROR(B_ENTRY_NOT_FOUND); 862 fs_volume* fsVolume = bsdVolume->mnt_fsvolume; 863 status = get_vnode(fsVolume, ino, reinterpret_cast<void**>(bsdNode)); 864 if (status != B_OK) 865 RETURN_ERROR(B_ENTRY_NOT_FOUND); 866 return B_OK; 867 } 868 869 870 /*! Convert inode into direntry location. 871 @param vnid A final inode number (possibly location-based, possibly artificial). 872 @post dirclust and diroffset specify the location of the direntry associated with vnid. If the 873 location is in the (FAT12/16) root directory, diroffset is relative to the start of the root 874 directory; otherwise it is cluster-relative. 875 */ 876 status_t 877 get_location(mount* bsdVolume, ino_t vnid, u_long* dirclust, u_long* diroffset) 878 { 879 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 880 ino_t location = -1; 881 882 vcache_vnid_to_loc(bsdVolume, vnid, &location); 883 884 if (dirclust != NULL && diroffset != NULL) { 885 // do the reverse of DETOI 886 if (static_cast<unsigned long>(location) < fatVolume->pm_RootDirEnts) { 887 // this is a regular file in a fixed root directory 888 *dirclust = MSDOSFSROOT; 889 *diroffset = location << 5; 890 } else { 891 location -= fatVolume->pm_RootDirEnts; 892 location <<= 5; 893 *dirclust = (location / fatVolume->pm_bpcluster) + 2; 894 *diroffset = location % fatVolume->pm_bpcluster; 895 } 896 } 897 898 return B_OK; 899 } 900 901 902 bool 903 node_exists(mount* bsdVolume, uint64_t inode) 904 { 905 bool constructed = false; 906 vcache_get_constructed(bsdVolume, inode, &constructed); 907 908 return constructed; 909 } 910 911 912 /*! Analagous to FreeBSD's vfs_timestamp, but returns the local time instead of GMT. 913 914 */ 915 void 916 timestamp_local(timespec* tsp) 917 { 918 bigtime_t usecs = real_time_clock_usecs(); 919 tsp->tv_sec = (usecs / 1000000LL); 920 tsp->tv_nsec = (usecs - tsp->tv_sec * 1000000LL) * 1000LL; 921 922 return; 923 } 924 925 926 void 927 local_to_GMT(const timespec* tspLocal, timespec* tspGMT) 928 { 929 *tspGMT = *tspLocal; 930 931 int32 offset = 0; 932 933 #ifdef _KERNEL_MODE 934 offset = static_cast<int32>(get_timezone_offset()); 935 #elif defined USER 936 time_t localTime; 937 time(&localTime); 938 939 struct tm localTm; 940 localtime_r(&localTime, &localTm); 941 offset = localTm.tm_gmtoff; 942 #endif 943 944 tspGMT->tv_sec -= offset; 945 946 return; 947 } 948 949 950 /*! Allocate and insert a struct buf into a buffer list. 951 @param deviceNode The node that represents the mounted device, whose v_bufobj member is to be 952 updated. 953 @param size The required size of buf::b_data. 954 @pre The msdosfsmount has been initialized and v_bufobj.bo_lock is write locked. 955 @post A new buf is inserted at the head of the bufobj SLIST that corresponds to 'size.' C-style 956 allocation so the buf can be freed in C code. 957 */ 958 status_t 959 slist_insert_buf(vnode* deviceNode, size_t size) 960 { 961 buf_list* list = NULL; 962 uint32* count = NULL; 963 msdosfsmount* fatVolume 964 = reinterpret_cast<msdosfsmount*>(deviceNode->v_rdev->si_mountpt->mnt_data); 965 966 if (size == 0) { 967 list = &deviceNode->v_bufobj.bo_emptybufs; 968 count = &deviceNode->v_bufobj.bo_empties; 969 } else if (size == fatVolume->pm_fatblocksize) { 970 list = &deviceNode->v_bufobj.bo_fatbufs; 971 count = &deviceNode->v_bufobj.bo_fatblocks; 972 } else if (size == fatVolume->pm_bpcluster) { 973 list = &deviceNode->v_bufobj.bo_clusterbufs; 974 count = &deviceNode->v_bufobj.bo_clusters; 975 } else { 976 return B_UNSUPPORTED; 977 } 978 979 buf* newBuf = reinterpret_cast<buf*>(calloc(1, sizeof(buf))); 980 if (newBuf == NULL) 981 return B_NO_MEMORY; 982 if (size != 0) { 983 newBuf->b_data = reinterpret_cast<caddr_t>(calloc(size, sizeof(char))); 984 if (newBuf->b_data == NULL) { 985 free(newBuf); 986 return B_NO_MEMORY; 987 } 988 } 989 newBuf->b_bufsize = size; 990 // The other members of newBuf will be initialized by getblkx 991 992 SLIST_INSERT_HEAD(list, newBuf, link); 993 (*count)++; 994 995 return B_OK; 996 } 997 998 999 status_t 1000 fill_gap_with_zeros(vnode* bsdNode, off_t pos, off_t newSize) 1001 { 1002 while (pos < newSize) { 1003 size_t size; 1004 if (newSize > pos + 1024 * 1024 * 1024) 1005 size = 1024 * 1024 * 1024; 1006 else 1007 size = newSize - pos; 1008 1009 status_t status = file_cache_write(bsdNode->v_cache, NULL, pos, NULL, &size); 1010 if (status < B_OK) 1011 return status; 1012 1013 pos += size; 1014 } 1015 1016 return B_OK; 1017 } 1018 1019 1020 /*! Sync the block cache blocks that hold the data of a directory file. 1021 1022 */ 1023 status_t 1024 sync_clusters(vnode* bsdNode) 1025 { 1026 mount* bsdVolume = bsdNode->v_mount; 1027 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data); 1028 msdosfsmount* fatVolume = fatNode->de_pmp; 1029 status_t status = B_OK; 1030 1031 ASSERT(bsdNode->v_type == VDIR); 1032 1033 u_long cluster = fatNode->de_dirclust; 1034 1035 if (cluster == MSDOSFSROOT) { 1036 status = block_cache_sync_etc(bsdVolume->mnt_cache, fatVolume->pm_rootdirblk, 1037 fatVolume->pm_rootdirsize); 1038 } else { 1039 status_t fatStatus = B_OK; 1040 while ((IS_DATA_CLUSTER(cluster)) && status == B_OK && fatStatus == B_OK) { 1041 status = block_cache_sync_etc(bsdVolume->mnt_cache, de_cn2bn(fatVolume, cluster), 1042 BLOCKS_PER_CLUSTER(fatVolume)); 1043 fatStatus = B_FROM_POSIX_ERROR(fatentry(FAT_GET, fatVolume, cluster, &cluster, 0)); 1044 } 1045 if (fatStatus != B_OK) 1046 REPORT_ERROR(fatStatus); 1047 } 1048 1049 RETURN_ERROR(status); 1050 } 1051 1052 1053 /*! Discard the block cache blocks that hold a directory's data. Has no effect on the root 1054 directory in FAT12/16; those blocks will be discarded when the volume is unmounted. 1055 */ 1056 status_t 1057 discard_clusters(vnode* bsdNode, off_t newLength) 1058 { 1059 mount* bsdVolume = bsdNode->v_mount; 1060 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data); 1061 msdosfsmount* fatVolume = fatNode->de_pmp; 1062 status_t status = B_OK; 1063 1064 ASSERT(bsdNode->v_type == VDIR); 1065 1066 // if we arrived here from detrunc, de_StartCluster has already been reset. 1067 u_long cluster = fatNode->de_dirclust; 1068 1069 // Typically we are discarding all clusters associated with a directory. However, in 1070 // the case of an error, the driver might shrink a directory to undo an attempted expansion, 1071 // as in createde. 1072 for (uint32 skip = howmany(newLength, fatVolume->pm_bpcluster); skip > 0 && status == B_OK; 1073 skip--) { 1074 status = B_FROM_POSIX_ERROR(fatentry(FAT_GET, fatVolume, cluster, &cluster, 0)); 1075 } 1076 1077 while ((IS_DATA_CLUSTER(cluster)) && status == B_OK) { 1078 block_cache_discard(bsdVolume->mnt_cache, de_cn2bn(fatVolume, cluster), 1079 BLOCKS_PER_CLUSTER(fatVolume)); 1080 status = B_FROM_POSIX_ERROR(fatentry(FAT_GET, fatVolume, cluster, &cluster, 0)); 1081 } 1082 1083 RETURN_ERROR(status); 1084 } 1085 1086 1087 /*! For use in the FAT userlandfs module. userlandfs does not provide check_access_permissions(). 1088 Limitation: ignores permissions granted by the user's group 1089 */ 1090 status_t 1091 check_access_permissions_internal(int accessMode, mode_t mode, gid_t nodeGroupID, uid_t nodeUserID) 1092 { 1093 // get node permissions 1094 int userPermissions = (mode & S_IRWXU) >> 6; 1095 int groupPermissions = (mode & S_IRWXG) >> 3; 1096 int otherPermissions = mode & S_IRWXO; 1097 1098 // get the node permissions for this uid/gid 1099 int permissions = 0; 1100 uid_t uid = geteuid(); 1101 1102 if (uid == 0) { 1103 // user is root 1104 // root has always read/write permission, but at least one of the 1105 // X bits must be set for execute permission 1106 permissions = userPermissions | groupPermissions | otherPermissions | S_IROTH | S_IWOTH; 1107 if (S_ISDIR(mode)) 1108 permissions |= S_IXOTH; 1109 } else if (uid == nodeUserID) { 1110 // user is node owner 1111 permissions = userPermissions; 1112 } else { 1113 // user is one of the others 1114 permissions = otherPermissions; 1115 } 1116 1117 // userlandfs does not provide is_user_in_group(), so we can't check group permissions 1118 1119 return (accessMode & ~permissions) == 0 ? B_OK : B_PERMISSION_DENIED; 1120 } 1121 1122 1123 /*! Populates file mode bits only (not file type bits). 1124 1125 */ 1126 void 1127 mode_bits(const vnode* bsdNode, mode_t* mode) 1128 { 1129 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data); 1130 msdosfsmount* fatVolume = fatNode->de_pmp; 1131 1132 *mode = S_IRUSR | S_IRGRP | S_IROTH; 1133 1134 if ((fatNode->de_Attributes & ATTR_READONLY) == 0) 1135 *mode |= S_IWUSR; 1136 1137 // In FAT, there is no place to store an executable flag on disk. FreeBSD makes all FAT files 1138 // executable, but Tracker will complain if, for example, a text file is executable. 1139 // To avoid that, we go by the MIME type. 1140 if (bsdNode->v_type == VDIR 1141 || (bsdNode->v_type == VREG && bsdNode->v_mime != NULL 1142 && strcmp(bsdNode->v_mime, "application/octet-stream") == 0)) { 1143 *mode |= S_IXUSR | S_IXGRP | S_IXOTH; 1144 } 1145 1146 *mode &= (bsdNode->v_type == VDIR) ? fatVolume->pm_dirmask : fatVolume->pm_mask; 1147 1148 return; 1149 } 1150 1151 1152 /*! Set the mime type of a node; has no effect in fat_shell. 1153 @param update True if this is an update to a pre-existing mime setting. 1154 */ 1155 status_t 1156 set_mime_type(vnode* bsdNode, bool update) 1157 { 1158 #ifndef FS_SHELL 1159 mount* bsdVolume = reinterpret_cast<mount*>(bsdNode->v_mount); 1160 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data); 1161 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(fatNode->de_pmp); 1162 1163 if (bsdNode->v_type == VREG) { 1164 char unixShortname[SHORTNAME_CSTRING + 1]; 1165 // +1 for the period added by dos2unixfn 1166 dos2unixfn(fatNode->de_Name, reinterpret_cast<u_char*>(unixShortname), 0, fatVolume); 1167 1168 set_mime(&bsdNode->v_mime, unixShortname); 1169 1170 notify_attribute_changed(bsdVolume->mnt_fsvolume->id, bsdNode->v_parent, fatNode->de_inode, 1171 "BEOS:TYPE", update ? B_ATTR_CHANGED : B_ATTR_CREATED); 1172 } 1173 #endif // FS_SHELL 1174 1175 return B_OK; 1176 } 1177 1178 1179 /*! Set a user-specified code page for translating FAT short filenames in UserlandFS. 1180 The FAT filesystem assigns both a short name and a long name to each file/directory. 1181 The short names are encoded in an 8-bit or DBCS OEM code page, aka DOS code page. 1182 Short names must be unique within a directory. 1183 If the user assigns a name containing a character not in the OEM code page, then 1184 the short name will substitute an underscore character. 1185 Users only see the long name, but collisions between short entry names are more likely if 1186 the OEM code page is not suitable for the user's language. 1187 FAT FS will attempt to resolve collisions by adding a generation number to the new 1188 short filename (also invisible to the user). 1189 Many code pages are supported by libiconv, which we use in the the userlandfs module. 1190 libiconv is not available in the FS shell or the kernel; in those cases the driver defaults to 1191 an internal copy of code page 850. 1192 */ 1193 status_t 1194 iconv_init(msdosfsmount* fatVolume, const char* oemPreference) 1195 { 1196 fatVolume->pm_u2w = NULL; 1197 fatVolume->pm_w2u = NULL; 1198 fatVolume->pm_u2d = NULL; 1199 fatVolume->pm_d2u = NULL; 1200 1201 #ifndef USER 1202 return B_OK; 1203 #else 1204 if ((fatVolume->pm_flags & MSDOSFSMNT_KICONV) == 0 || strcmp(oemPreference, "") == 0) 1205 return B_OK; 1206 1207 msdosfs_iconv = new(std::nothrow) iconv_functions; 1208 // fills in for FreeBSD's VFS_DECLARE_ICONV macro 1209 if (msdosfs_iconv == NULL) 1210 RETURN_ERROR(B_NO_MEMORY); 1211 ObjectDeleter<iconv_functions> deleter(msdosfs_iconv); 1212 1213 msdosfs_iconv->open = fat_iconv_open; 1214 msdosfs_iconv->close = fat_iconv_close; 1215 msdosfs_iconv->conv = iconv_conv; 1216 msdosfs_iconv->convchr = iconv_convchr; 1217 msdosfs_iconv->convchr_case = iconv_convchr_case; 1218 1219 PRINT("setting code page to %s\n", oemPreference); 1220 1221 const char* haiku = "UTF-8"; 1222 const char* windows = "UCS-2"; 1223 const char* dos = oemPreference; 1224 1225 status_t status = B_OK; 1226 status = B_FROM_POSIX_ERROR(msdosfs_iconv->open(windows, haiku, &fatVolume->pm_u2w)); 1227 if (status != B_OK) 1228 RETURN_ERROR(errno); 1229 1230 status = B_FROM_POSIX_ERROR(msdosfs_iconv->open(haiku, windows, &fatVolume->pm_w2u)); 1231 if (status != B_OK) 1232 RETURN_ERROR(errno); 1233 1234 status = B_FROM_POSIX_ERROR(msdosfs_iconv->open(dos, haiku, &fatVolume->pm_u2d)); 1235 if (status != B_OK) 1236 RETURN_ERROR(errno); 1237 1238 status = B_FROM_POSIX_ERROR(msdosfs_iconv->open(haiku, dos, &fatVolume->pm_d2u)); 1239 if (status != B_OK) 1240 RETURN_ERROR(errno); 1241 1242 deleter.Detach(); 1243 1244 return B_OK; 1245 #endif // USER 1246 } 1247