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