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