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 #ifdef FS_SHELL 59 #include "fssh_api_wrapper.h" 60 #else // !FS_SHELL 61 #include <dirent.h> 62 #include <malloc.h> 63 #include <new> 64 #include <stdlib.h> 65 #endif // !FS_SHELL 66 67 #ifndef FS_SHELL 68 #include <NodeMonitor.h> 69 #include <OS.h> 70 #include <TypeConstants.h> 71 #include <driver_settings.h> 72 #include <fs_info.h> 73 #include <fs_interface.h> 74 #include <fs_volume.h> 75 #include <io_requests.h> 76 #endif // !FS_SHELL 77 78 #if defined USER && __GNUC__ == 2 79 // required for fs_ops_support.h 80 #define alignof(type) __alignof__(type) 81 #endif // USER && __GNUC__ == 2 82 #include <fs_ops_support.h> 83 #ifdef FS_SHELL 84 #include "fssh_auto_deleter.h" 85 #include "syscalls.h" 86 #else // !FS_SHELL 87 #include <AutoDeleter.h> 88 #include <arch_vm.h> 89 #include <kernel.h> 90 #include <syscalls.h> 91 #include <util/AutoLock.h> 92 #include <vfs.h> 93 #endif // !FS_SHELL 94 95 // FreeBSD flag that turns on full implementation of ported code 96 #define _KERNEL 97 98 extern "C" 99 { 100 #include "sys/param.h" 101 #include "sys/buf.h" 102 #include "sys/clock.h" 103 #include "sys/conf.h" 104 #include "sys/iconv.h" 105 #include "sys/mount.h" 106 #include "sys/mutex.h" 107 #include "sys/namei.h" 108 #include "sys/vnode.h" 109 110 #include "fs/msdosfs/bootsect.h" 111 #include "fs/msdosfs/bpb.h" 112 #include "fs/msdosfs/denode.h" 113 #include "fs/msdosfs/direntry.h" 114 #include "fs/msdosfs/fat.h" 115 #include "fs/msdosfs/msdosfsmount.h" 116 } 117 118 #include "debug.h" 119 #include "dosfs.h" 120 #ifdef FS_SHELL 121 #include "fssh_defines.h" 122 #endif // FS_SHELL 123 #include "mkdos.h" 124 #include "support.h" 125 #include "vcache.h" 126 127 128 static status_t iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset, 129 size_t size, struct file_io_vec* vecs, size_t* _count); 130 static status_t iterative_io_finished_hook(void* cookie, io_request* request, status_t status, 131 bool partialTransfer, size_t bytesTransferred); 132 133 static status_t _dosfs_sync(mount* volume, bool data = true); 134 static status_t _dosfs_fsync(vnode* bsdNode); 135 static status_t _dosfs_read_vnode(mount* bsdVolume, const ino_t id, vnode** newNode, bool createFileCache = true); 136 137 static status_t bsd_volume_init(fs_volume* fsVolume, const uint32 flags, mount** volume); 138 status_t bsd_volume_uninit(mount* volume); 139 static status_t bsd_device_init(mount* bsdVolume, const dev_t devID, const char* deviceFile, 140 cdev** bsdDevice, bool* _readOnly); 141 status_t bsd_device_uninit(cdev* device); 142 static status_t dev_bsd_node_init(cdev* bsdDevice, vnode** devNode); 143 status_t dev_bsd_node_uninit(vnode* devNode); 144 static status_t fat_volume_init(vnode* devvp, mount* bsdVolume, const uint64_t fatFlags, 145 const char* oemPref); 146 status_t fat_volume_uninit(msdosfsmount* volume); 147 148 149 typedef struct IdentifyCookie { 150 uint32 fBytesPerSector; 151 uint32 fTotalSectors; 152 char fName[12]; 153 } IdentifyCookie; 154 155 typedef struct FileCookie { 156 uint32 fMode; // open mode 157 u_long fLastSize; // file size at last notify_stat_changed call 158 u_short fMtimeAtOpen; // inital modification time 159 u_short fMdateAtOpen; // initial modification date 160 bigtime_t fLastNotification; // time of last notify_stat_changed call 161 } FileCookie; 162 163 typedef struct DirCookie { 164 uint32 fIndex; // read this entry next 165 } DirCookie; 166 167 typedef struct AttrCookie { 168 uint32 fMode; // open mode 169 int32 fType; // attribute type 170 #define FAT_ATTR_MIME 0x1234 171 } AttrCookie; 172 173 174 typedef CObjectDeleter<mount, status_t, &bsd_volume_uninit> StructMountDeleter; 175 typedef CObjectDeleter<cdev, status_t, &bsd_device_uninit> StructCdevDeleter; 176 typedef CObjectDeleter<vnode, status_t, &dev_bsd_node_uninit> DevVnodeDeleter; 177 typedef CObjectDeleter<msdosfsmount, status_t, &fat_volume_uninit> StructMsdosfsmountDeleter; 178 179 180 struct iconv_functions* msdosfs_iconv; 181 182 183 static status_t 184 dosfs_mount(fs_volume* volume, const char* device, uint32 flags, const char* args, 185 ino_t* _rootVnodeID) 186 { 187 #ifdef FS_SHELL 188 FUNCTION_START("device %" B_PRIdDEV "\n", volume->id); 189 #else 190 FUNCTION_START("device %" B_PRIdDEV ", partition %" B_PRId32 "\n", volume->id, 191 volume->partition); 192 #endif 193 194 status_t status = B_OK; 195 196 int opSyncMode = 0; 197 char oemPref[11] = ""; 198 void* handle = load_driver_settings("dos"); 199 if (handle != NULL) { 200 opSyncMode = strtoul(get_driver_parameter(handle, "op_sync_mode", "0", "0"), NULL, 0); 201 if (opSyncMode < 0 || opSyncMode > 2) 202 opSyncMode = 0; 203 204 strlcpy(oemPref, get_driver_parameter(handle, "OEM_code_page", "", ""), 11); 205 206 unload_driver_settings(handle); 207 } 208 209 uint64 fatFlags = 0; 210 // libiconv support is implemented only for the userlandfs module 211 #ifdef USER 212 fatFlags |= MSDOSFSMNT_KICONV; 213 if (strcmp(oemPref, "") == 0) 214 strlcpy(oemPref, "CP1252", 11); 215 #endif // USER 216 217 // args is a command line option; dosfs doesn't use any so we can ignore it 218 219 bool readOnly = (flags & B_MOUNT_READ_ONLY) != 0; 220 if ((flags & ~B_MOUNT_READ_ONLY) != 0) { 221 INFORM("unsupported mount flag(s) %" B_PRIx32 "\n", (flags & ~B_MOUNT_READ_ONLY)); 222 return B_UNSUPPORTED; 223 } 224 225 // Initialize the struct mount, which is an adapted FreeBSD VFS object. It is present in the 226 // port because the ported BSD code relies on it. 227 mount* bsdVolume; 228 status = bsd_volume_init(volume, flags, &bsdVolume); 229 if (status != B_OK) 230 RETURN_ERROR(status); 231 StructMountDeleter bsdVolumeDeleter(bsdVolume); 232 233 // initialize a BSD-style device struct 234 cdev* bsdDevice = NULL; 235 status = bsd_device_init(bsdVolume, volume->id, device, &bsdDevice, &readOnly); 236 if (status != B_OK) 237 RETURN_ERROR(status); 238 StructCdevDeleter bsdDeviceDeleter(bsdDevice); 239 240 if (readOnly == true) { 241 bsdVolume->mnt_flag |= MNT_RDONLY; 242 fatFlags |= MSDOSFSMNT_RONLY; 243 } 244 245 // A shell/FUSE host system might not call dosfs_sync automatically at shutdown/reboot if the 246 // user forgets to unmount a volume, so we always use op sync mode for those targets. 247 #ifdef FS_SHELL 248 opSyncMode = 2; 249 #endif // FS_SHELL 250 251 // see if we need to go into op sync mode 252 switch (opSyncMode) { 253 case 1: 254 if (bsdDevice->si_geometry->removable == false) { 255 // we're not removable, so skip op_sync 256 break; 257 } 258 // supposed to fall through 259 260 case 2: 261 PRINT("mounted with op sync enabled\n"); 262 bsdVolume->mnt_flag |= MNT_SYNCHRONOUS; 263 fatFlags |= MSDOSFSMNT_WAITONFAT; 264 break; 265 266 case 0: 267 default: 268 bsdVolume->mnt_flag |= MNT_ASYNC; 269 break; 270 } 271 272 // The driver needs access to a BSD-format vnode representing the device file, which in BSD 273 // would be a vnode on another volume. We manually generate a stand-in. 274 vnode* devNode; 275 status = dev_bsd_node_init(bsdDevice, &devNode); 276 if (status != B_OK) 277 RETURN_ERROR(status); 278 DevVnodeDeleter devVnodeDeleter(devNode); 279 280 // initialize the FAT private volume data 281 status = fat_volume_init(devNode, bsdVolume, fatFlags, oemPref); 282 if (status != B_OK) 283 RETURN_ERROR(status); 284 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 285 StructMsdosfsmountDeleter fatVolumeDeleter(fatVolume); 286 287 // create caches of struct bufs for the driver to use in bread() 288 rw_lock_write_lock(&devNode->v_bufobj.bo_lock.haikuRW); 289 for (uint32 i = 0; i < BUF_CACHE_SIZE; ++i) { 290 status = slist_insert_buf(devNode, fatVolume->pm_bpcluster); 291 if (status != B_OK) 292 RETURN_ERROR(status); 293 status = slist_insert_buf(devNode, fatVolume->pm_fatblocksize); 294 if (status != B_OK) 295 RETURN_ERROR(status); 296 status = slist_insert_buf(devNode, 0); 297 if (status != B_OK) 298 RETURN_ERROR(status); 299 } 300 rw_lock_write_unlock(&devNode->v_bufobj.bo_lock.haikuRW); 301 302 volume->private_volume = bsdVolume; 303 volume->ops = &gFATVolumeOps; 304 305 // publish root vnode 306 307 u_long dirClust = FAT32(fatVolume) == true ? fatVolume->pm_rootdirblk : MSDOSFSROOT; 308 u_long dirOffset = MSDOSFSROOT_OFS; 309 ino_t rootInode = DETOI(fatVolume, dirClust, dirOffset); 310 311 status = add_to_vcache(bsdVolume, rootInode, rootInode); 312 if (status != B_OK) 313 RETURN_ERROR(status); 314 315 vnode* bsdRootNode; 316 status = _dosfs_read_vnode(bsdVolume, rootInode, &bsdRootNode); 317 if (status != B_OK) 318 RETURN_ERROR(status); 319 denode* fatRootNode = reinterpret_cast<denode*>(bsdRootNode->v_data); 320 ASSERT(fatRootNode->de_dirclust == dirClust && fatRootNode->de_diroffset == dirOffset); 321 322 status = publish_vnode(volume, rootInode, bsdRootNode, &gFATVnodeOps, S_IFDIR, 0); 323 if (status != B_OK) 324 RETURN_ERROR(status); 325 326 PRINT("root vnode id = %" B_PRIdINO ", @ %p\n", fatRootNode->de_inode, bsdRootNode); 327 328 *_rootVnodeID = fatRootNode->de_inode; 329 330 #ifdef _KERNEL_MODE 331 // initialize mnt_stat.f_mntonname, for use by msdosfs_integrity_error 332 dev_t mountpt; 333 ino_t mountino; 334 vfs_get_mount_point(fatVolume->pm_dev->si_id, &mountpt, &mountino); 335 vfs_entry_ref_to_path(mountpt, mountino, NULL, true, bsdVolume->mnt_stat.f_mntonname, 336 B_PATH_NAME_LENGTH); 337 #endif // _KERNEL_MODE 338 339 bsdVolumeDeleter.Detach(); 340 bsdDeviceDeleter.Detach(); 341 devVnodeDeleter.Detach(); 342 fatVolumeDeleter.Detach(); 343 344 return B_OK; 345 } 346 347 348 static float 349 dosfs_identify_partition(int fd, partition_data* partition, void** _cookie) 350 { 351 FUNCTION_START("dosfs_identify_partition\n"); 352 353 // read in the boot sector 354 uint8 buf[512]; 355 if (read_pos(fd, 0, buf, 512) != 512) 356 return -1; 357 358 FatType type; 359 bool dos33; 360 status_t status = check_bootsector(buf, type, dos33); 361 if (status != B_OK) 362 return status; 363 364 // partially set up a msdosfsmount, enough to read the volume label from the root directory 365 msdosfsmount dummyVolume; 366 dummyVolume.pm_mountp = NULL; 367 switch (type) { 368 case fat12: 369 dummyVolume.pm_fatmask = FAT12_MASK; 370 break; 371 case fat16: 372 dummyVolume.pm_fatmask = FAT16_MASK; 373 break; 374 case fat32: 375 dummyVolume.pm_fatmask = FAT32_MASK; 376 break; 377 default: 378 return -1; 379 } 380 status = parse_bpb(&dummyVolume, reinterpret_cast<union bootsector*>(buf), dos33); 381 if (status != B_OK) 382 return status; 383 dummyVolume.pm_BlkPerSec = dummyVolume.pm_BytesPerSec / DEV_BSIZE; 384 dummyVolume.pm_rootdirsize = howmany(dummyVolume.pm_RootDirEnts * sizeof(direntry), DEV_BSIZE); 385 // Will be 0 for a FAT32 volume. 386 dummyVolume.pm_bpcluster 387 = dummyVolume.pm_bpb.bpbSecPerClust * dummyVolume.pm_BlkPerSec * DEV_BSIZE; 388 dummyVolume.pm_bnshift = ffs(DEV_BSIZE) - 1; 389 // for FAT12/16, parse_bpb doesn't initialize pm_rootdirblk 390 if (type != fat32) { 391 dummyVolume.pm_BlkPerSec = dummyVolume.pm_BytesPerSec / DEV_BSIZE; 392 dummyVolume.pm_fatblk = dummyVolume.pm_ResSectors * dummyVolume.pm_BlkPerSec; 393 dummyVolume.pm_rootdirblk 394 = dummyVolume.pm_fatblk + dummyVolume.pm_FATs * dummyVolume.pm_FATsecs; 395 } 396 397 char name[LABEL_CSTRING]; 398 strcpy(name, "no name"); 399 read_label(&dummyVolume, fd, buf, name); 400 sanitize_label(name); 401 402 IdentifyCookie* cookie = new(std::nothrow) IdentifyCookie; 403 if (!cookie) 404 return -1; 405 cookie->fBytesPerSector = dummyVolume.pm_BytesPerSec; 406 cookie->fTotalSectors = dummyVolume.pm_HugeSectors; 407 strlcpy(cookie->fName, name, 12); 408 409 *_cookie = cookie; 410 411 return 0.8f; 412 } 413 414 415 static status_t 416 dosfs_scan_partition(int fd, partition_data* partition, void* _cookie) 417 { 418 IdentifyCookie* cookie = reinterpret_cast<IdentifyCookie*>(_cookie); 419 420 partition->status = B_PARTITION_VALID; 421 partition->flags |= B_PARTITION_FILE_SYSTEM; 422 partition->content_size = static_cast<off_t>(cookie->fTotalSectors) * cookie->fBytesPerSector; 423 partition->block_size = cookie->fBytesPerSector; 424 partition->content_name = strdup(cookie->fName); 425 if (partition->content_name == NULL) 426 return B_NO_MEMORY; 427 428 return B_OK; 429 } 430 431 432 static void 433 dosfs_free_identify_partition_cookie(partition_data* partition, void* _cookie) 434 { 435 delete reinterpret_cast<IdentifyCookie*>(_cookie); 436 437 return; 438 } 439 440 441 static status_t 442 dosfs_unmount(fs_volume* volume) 443 { 444 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 445 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 446 vnode* deviceNode = fatVolume->pm_devvp; 447 cdev* bsdDevice = fatVolume->pm_dev; 448 449 #ifdef FS_SHELL 450 FUNCTION_START("device %" B_PRIdDEV "\n", volume->id); 451 #else 452 FUNCTION_START("device %" B_PRIdDEV ", partition %" B_PRId32 "\n", volume->id, 453 volume->partition); 454 #endif 455 456 status_t status = B_OK; 457 status_t returnStatus = B_OK; 458 459 MutexLocker locker(bsdVolume->mnt_mtx.haikuMutex); 460 461 status = fat_volume_uninit(fatVolume); 462 if (status != B_OK) 463 returnStatus = status; 464 465 // pseudo-BSD layer cleanup 466 status = bsd_device_uninit(bsdDevice); 467 if (status != B_OK) 468 returnStatus = status; 469 status = dev_bsd_node_uninit(deviceNode); 470 if (status != B_OK) 471 returnStatus = status; 472 locker.Unlock(); 473 status = bsd_volume_uninit(bsdVolume); 474 if (status != B_OK) 475 returnStatus = status; 476 477 RETURN_ERROR(returnStatus); 478 } 479 480 481 static status_t 482 dosfs_read_fs_stat(fs_volume* volume, struct fs_info* info) 483 { 484 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 485 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 486 cdev* bsdDevice = fatVolume->pm_dev; 487 488 FUNCTION(); 489 490 MutexLocker locker(bsdVolume->mnt_mtx.haikuMutex); 491 492 info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_MIME; 493 if ((bsdVolume->mnt_flag & MNT_RDONLY) != 0) 494 info->flags |= B_FS_IS_READONLY; 495 496 if (bsdDevice->si_geometry->removable == true) 497 info->flags |= B_FS_IS_REMOVABLE; 498 499 info->block_size = fatVolume->pm_bpcluster; 500 501 info->io_size = FAT_IO_SIZE; 502 503 info->total_blocks = fatVolume->pm_maxcluster + 1 - 2; 504 // convert from index to count and adjust for 2 reserved cluster numbers 505 506 info->free_blocks = fatVolume->pm_freeclustercount; 507 508 info->total_nodes = LONGLONG_MAX; 509 510 info->free_nodes = LONGLONG_MAX; 511 512 strlcpy(info->volume_name, fatVolume->pm_dev->si_name, sizeof(info->volume_name)); 513 sanitize_label(info->volume_name); 514 515 strlcpy(info->device_name, fatVolume->pm_dev->si_device, sizeof(info->device_name)); 516 517 strlcpy(info->fsh_name, "fat", sizeof(info->fsh_name)); 518 519 return B_OK; 520 } 521 522 523 static status_t 524 dosfs_write_fs_stat(fs_volume* volume, const struct fs_info* info, uint32 mask) 525 { 526 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 527 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 528 529 FUNCTION_START("with mask %" B_PRIx32 "\n", mask); 530 531 MutexLocker locker(bsdVolume->mnt_mtx.haikuMutex); 532 533 if ((mask & FS_WRITE_FSINFO_NAME) == 0) 534 return B_OK; 535 536 // if it's a r/o file system, then don't allow volume renaming 537 if ((bsdVolume->mnt_flag & MNT_RDONLY) != 0) 538 return B_READ_ONLY_DEVICE; 539 540 // convert volume_name into an all-caps space-padded array 541 char name[LABEL_LENGTH]; 542 int i, j; 543 memset(name, ' ', LABEL_LENGTH); 544 PRINT("wfsstat: setting name to %s\n", info->volume_name); 545 for (i = j = 0; (i < LABEL_LENGTH) && (info->volume_name[j]); j++) { 546 char c = info->volume_name[j]; 547 if ((c >= 'a') && (c <= 'z')) 548 c += 'A' - 'a'; 549 // spaces acceptable in volume names 550 if (strchr(sAcceptable, c) || (c == ' ')) 551 name[i++] = c; 552 } 553 if (i == 0) // bad name, kiddo 554 return B_BAD_VALUE; 555 PRINT("wfsstat: converted to [%11.11s]\n", name); 556 557 // update the BPB, unless the volume is too old to have a label field in the BPB 558 void* blockCache = bsdVolume->mnt_cache; 559 u_char* buffer; 560 status_t status 561 = block_cache_get_writable_etc(blockCache, 0, 0, 1, -1, reinterpret_cast<void**>(&buffer)); 562 if (status != B_OK) 563 return status; 564 // check for the extended boot signature 565 uint32 ebsOffset = FAT32(fatVolume) != 0 ? 0x42 : 0x26; 566 uint32 labelOffset = ebsOffset + 5; 567 char* memoryLabel = fatVolume->pm_dev->si_name; 568 if (buffer[ebsOffset] == EXBOOTSIG) { 569 // double check the position by verifying the name presently stored there 570 char bpbLabel[LABEL_CSTRING]; 571 memcpy(bpbLabel, buffer + labelOffset, LABEL_LENGTH); 572 sanitize_label(bpbLabel); 573 if (strncmp(bpbLabel, memoryLabel, LABEL_LENGTH) == 0) { 574 memcpy(buffer + labelOffset, name, LABEL_LENGTH); 575 } else { 576 INFORM("wfsstat: BPB position check failed\n"); 577 block_cache_set_dirty(blockCache, 0, false, -1); 578 status = B_ERROR; 579 } 580 } 581 block_cache_put(blockCache, 0); 582 583 // update the label file if there is one 584 if (bsdVolume->mnt_volentry >= 0) { 585 uint8* rootDirBuffer; 586 daddr_t rootDirBlock = fatVolume->pm_rootdirblk; 587 if (FAT32(fatVolume) == true) 588 rootDirBlock = cntobn(fatVolume, fatVolume->pm_rootdirblk); 589 daddr_t dirOffset = bsdVolume->mnt_volentry * sizeof(direntry); 590 rootDirBlock += dirOffset / DEV_BSIZE; 591 592 status = block_cache_get_writable_etc(blockCache, rootDirBlock, 0, 1, -1, 593 reinterpret_cast<void**>(&rootDirBuffer)); 594 if (status == B_OK) { 595 direntry* label_direntry = reinterpret_cast<direntry*>(rootDirBuffer + dirOffset); 596 597 char rootLabel[LABEL_CSTRING]; 598 memcpy(rootLabel, label_direntry->deName, LABEL_LENGTH); 599 sanitize_label(rootLabel); 600 if (strncmp(rootLabel, memoryLabel, LABEL_LENGTH) == 0) { 601 memcpy(label_direntry->deName, name, LABEL_LENGTH); 602 } else { 603 INFORM("wfsstat: root directory position check failed\n"); 604 block_cache_set_dirty(blockCache, rootDirBlock, false, -1); 605 status = B_ERROR; 606 } 607 block_cache_put(blockCache, rootDirBlock); 608 } 609 } else { 610 // A future enhancement could be to create a label direntry if none exists already. 611 } 612 613 if (status == B_OK) { 614 memcpy(memoryLabel, name, LABEL_LENGTH); 615 sanitize_label(memoryLabel); 616 } 617 618 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0) 619 _dosfs_sync(bsdVolume, false); 620 621 RETURN_ERROR(status); 622 } 623 624 625 static status_t 626 dosfs_sync(fs_volume* volume) 627 { 628 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 629 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 630 631 FUNCTION(); 632 633 MutexLocker volumeLocker(bsdVolume->mnt_mtx.haikuMutex); 634 WriteLocker fatLocker(fatVolume->pm_fatlock.haikuRW); 635 636 RETURN_ERROR(_dosfs_sync(bsdVolume)); 637 } 638 639 640 /*! If data is true, include regular file data in the sync. Otherwise, only sync directories, 641 the FAT, and, if applicable, the fsinfo sector. 642 */ 643 status_t 644 _dosfs_sync(struct mount* bsdVolume, bool data) 645 { 646 status_t status = B_OK; 647 status_t returnStatus = B_OK; 648 649 status = write_fsinfo(reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data)); 650 if (status != B_OK) { 651 REPORT_ERROR(status); 652 returnStatus = status; 653 } 654 655 status = block_cache_sync(bsdVolume->mnt_cache); 656 if (status != B_OK) { 657 REPORT_ERROR(status); 658 returnStatus = status; 659 } 660 661 if (data == true) { 662 status = sync_all_files(bsdVolume); 663 if (status != B_OK) { 664 REPORT_ERROR(status); 665 returnStatus = status; 666 } 667 } 668 669 return returnStatus; 670 } 671 672 673 static status_t 674 dosfs_read_vnode(fs_volume* volume, ino_t id, fs_vnode* vnode, int* _type, uint32* _flags, 675 bool reenter) 676 { 677 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 678 struct vnode* bsdNode; 679 680 FUNCTION_START("id %" B_PRIdINO ", type %d, flags %" B_PRIx32 "\n", id, *_type, *_flags); 681 682 MutexLocker locker(bsdVolume->mnt_mtx.haikuMutex); 683 684 // In case 2 threads are concurrently executing get_vnode() with the same ID, verify 685 // after locking the volume that the node has not been constructed already. 686 if (node_exists(bsdVolume, id) == true) 687 return B_BAD_VALUE; 688 689 status_t status = _dosfs_read_vnode(bsdVolume, id, &bsdNode); 690 if (status != B_OK) 691 RETURN_ERROR(status); 692 693 ASSERT(static_cast<ino_t>(reinterpret_cast<denode*>(bsdNode->v_data)->de_inode) == id); 694 695 vnode->private_node = bsdNode; 696 vnode->ops = &gFATVnodeOps; 697 if (bsdNode->v_type == VDIR) 698 *_type = S_IFDIR; 699 else if (bsdNode->v_type == VREG) 700 *_type = S_IFREG; 701 else 702 panic("dosfs_read_vnode: unknown type\n"); 703 704 *_flags = 0; 705 706 return B_OK; 707 } 708 709 710 /*! Can be used internally by the FS to generate a private node. 711 712 */ 713 static status_t 714 _dosfs_read_vnode(mount* bsdVolume, const ino_t id, vnode** newNode, bool createFileCache) 715 { 716 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 717 718 status_t status = B_OK; 719 u_long dirClust, dirOffset; 720 if (id == root_inode(fatVolume)) { 721 dirClust = FAT32(fatVolume) == true ? fatVolume->pm_rootdirblk : MSDOSFSROOT; 722 dirOffset = MSDOSFSROOT_OFS; 723 } else { 724 status = get_location(bsdVolume, id, &dirClust, &dirOffset); 725 if (status != B_OK) 726 return status; 727 } 728 729 denode* fatNode; 730 status = B_FROM_POSIX_ERROR(deget(fatVolume, dirClust, dirOffset, LK_EXCLUSIVE, &fatNode)); 731 if (status != B_OK) 732 return status; 733 734 vnode* bsdNode = fatNode->de_vnode; 735 if (bsdNode->v_type == VREG) { 736 status = set_mime_type(bsdNode, false); 737 if (status != B_OK) 738 REPORT_ERROR(status); 739 740 if (createFileCache) { 741 bsdNode->v_cache 742 = file_cache_create(fatVolume->pm_dev->si_id, fatNode->de_inode, fatNode->de_FileSize); 743 bsdNode->v_file_map 744 = file_map_create(fatVolume->pm_dev->si_id, fatNode->de_inode, fatNode->de_FileSize); 745 } 746 } 747 748 // identify the parent directory 749 if (id == root_inode(fatVolume)) { 750 bsdNode->v_parent = id; 751 } else if (bsdNode->v_type == VREG) { 752 bsdNode->v_parent = fatVolume->pm_bpcluster * dirClust; 753 assign_inode(bsdVolume, &bsdNode->v_parent); 754 } 755 // For a directory other than the root directory, there is no easy way to 756 // ID the parent. That Will be done in later (in dosfs_walk / dosfs_mkdir). 757 758 bsdNode->v_state = VSTATE_CONSTRUCTED; 759 760 status = vcache_set_constructed(bsdVolume, fatNode->de_inode); 761 if (status != B_OK) { 762 free(fatNode); 763 free(bsdNode); 764 return status; 765 } 766 767 #ifdef DEBUG 768 status = vcache_set_node(bsdVolume, fatNode->de_inode, bsdNode); 769 if (status != B_OK) 770 REPORT_ERROR(status); 771 #endif // DEBUG 772 773 *newNode = bsdNode; 774 775 rw_lock_write_unlock(&bsdNode->v_vnlock->haikuRW); 776 777 return B_OK; 778 } 779 780 781 static status_t 782 dosfs_walk(fs_volume* volume, fs_vnode* dir, const char* name, ino_t* _id) 783 { 784 vnode* bsdDir = reinterpret_cast<vnode*>(dir->private_node); 785 denode* fatDir = reinterpret_cast<denode*>(bsdDir->v_data); 786 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 787 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 788 789 WriteLocker locker(bsdDir->v_vnlock->haikuRW); 790 // msdosfs_lookup_ino will modify de_fndoffset, de_fndcnt 791 792 if (bsdDir->v_type != VDIR) 793 RETURN_ERROR(B_NOT_A_DIRECTORY); 794 795 ComponentName bsdName((strcmp(name, "..") == 0 ? MAKEENTRY | ISDOTDOT : MAKEENTRY), NOCRED, 796 LOOKUP, 0, name); 797 798 daddr_t dirClust; 799 u_long dirOffset; 800 status_t status = B_FROM_POSIX_ERROR( 801 msdosfs_lookup_ino(bsdDir, NULL, bsdName.Data(), &dirClust, &dirOffset)); 802 if (status != B_OK) { 803 entry_cache_add_missing(volume->id, fatDir->de_inode, bsdName.Data()->cn_nameptr); 804 RETURN_ERROR(B_ENTRY_NOT_FOUND); 805 } 806 // msdosfs_lookup_ino will return 0 for cluster number if looking up .. in a directory 807 // whose parent is the root directory, even on FAT32 volumes (which reflects the 808 // value that is meant to be stored in the .. direntry, per the FAT spec) 809 if (FAT32(fatVolume) == true && dirClust == MSDOSFSROOT) 810 dirClust = fatVolume->pm_rootdirblk; 811 vnode* bsdResult; 812 status = assign_inode_and_get(bsdVolume, dirClust, dirOffset, &bsdResult); 813 if (status != B_OK) 814 RETURN_ERROR(status); 815 denode* fatResult = reinterpret_cast<denode*>(bsdResult->v_data); 816 817 if (bsdResult->v_type == VDIR) { 818 // dosfs_read_vnode does not set this for directories because it does not know the 819 // parent inode 820 bsdResult->v_parent = fatDir->de_inode; 821 } 822 823 *_id = fatResult->de_inode; 824 825 entry_cache_add(volume->id, fatDir->de_inode, name, fatResult->de_inode); 826 827 return B_OK; 828 } 829 830 831 static status_t 832 dosfs_release_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter) 833 { 834 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 835 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 836 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data); 837 838 FUNCTION_START("inode %" B_PRIdINO " @ %p\n", fatNode->de_inode, bsdNode); 839 840 status_t status = B_OK; 841 842 if ((bsdNode->v_vflag & VV_ROOT) == 0) { 843 WriteLocker locker(bsdNode->v_vnlock->haikuRW); 844 // needed only in this block 845 846 status = B_FROM_POSIX_ERROR(deupdat(fatNode, 0)); 847 if (status != B_OK) 848 RETURN_ERROR(status); 849 850 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0) 851 _dosfs_fsync(bsdNode); 852 } 853 854 if (bsdNode->v_type == VREG) { 855 status = file_cache_sync(bsdNode->v_cache); 856 file_cache_delete(bsdNode->v_cache); 857 file_map_delete(bsdNode->v_file_map); 858 } else { 859 status = discard_clusters(bsdNode, 0); 860 } 861 862 vcache_set_constructed(bsdVolume, fatNode->de_inode, false); 863 864 free(fatNode); 865 866 rw_lock_destroy(&bsdNode->v_vnlock->haikuRW); 867 868 free(bsdNode); 869 870 RETURN_ERROR(status); 871 } 872 873 874 status_t 875 dosfs_remove_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter) 876 { 877 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 878 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 879 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 880 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data); 881 882 FUNCTION_START("%" B_PRIu64 " @ %p\n", fatNode->de_inode, bsdNode); 883 884 WriteLocker locker(bsdNode->v_vnlock->haikuRW); 885 886 if (MOUNTED_READ_ONLY(fatVolume) != 0) 887 RETURN_ERROR(B_READ_ONLY_DEVICE); 888 889 status_t status = B_OK; 890 891 if (bsdNode->v_type == VREG) { 892 file_cache_delete(bsdNode->v_cache); 893 bsdNode->v_cache = NULL; 894 file_map_delete(bsdNode->v_file_map); 895 bsdNode->v_file_map = NULL; 896 } else { 897 status = discard_clusters(bsdNode, 0); 898 if (status != B_OK) 899 REPORT_ERROR(status); 900 } 901 902 // truncate the file 903 if (fatNode->de_refcnt <= 0 && fatNode->de_StartCluster != root_start_cluster(fatVolume)) { 904 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW); 905 status = B_FROM_POSIX_ERROR(detrunc(fatNode, static_cast<u_long>(0), 0, NOCRED)); 906 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW); 907 if (status != B_OK) 908 REPORT_ERROR(status); 909 } 910 if (status == B_OK) { 911 // remove vnode id from the cache 912 if (find_vnid_in_vcache(bsdVolume, fatNode->de_inode) == B_OK) 913 remove_from_vcache(bsdVolume, fatNode->de_inode); 914 915 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0) 916 _dosfs_sync(bsdVolume, false); 917 } 918 919 free(fatNode); 920 921 locker.Unlock(); 922 rw_lock_destroy(&bsdNode->v_vnlock->haikuRW); 923 924 free(bsdNode); 925 926 RETURN_ERROR(status); 927 } 928 929 930 static bool 931 dosfs_can_page(fs_volume* vol, fs_vnode* vnode, void* cookie) 932 { 933 // ToDo: we're obviously not even asked... 934 return false; 935 } 936 937 938 static status_t 939 dosfs_read_pages(fs_volume* volume, fs_vnode* vnode, void* cookie, off_t pos, const iovec* vecs, 940 size_t count, size_t* _numBytes) 941 { 942 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 943 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 944 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 945 946 FUNCTION_START("%p\n", bsdNode); 947 948 if (bsdNode->v_cache == NULL) 949 return B_BAD_VALUE; 950 951 ReadLocker locker(bsdNode->v_vnlock->haikuRW); 952 953 uint32 vecIndex = 0; 954 size_t vecOffset = 0; 955 size_t bytesLeft = *_numBytes; 956 status_t status; 957 958 while (true) { 959 struct file_io_vec fileVecs[8]; 960 size_t fileVecCount = 8; 961 bool bufferOverflow; 962 size_t bytes = bytesLeft; 963 964 status 965 = file_map_translate(bsdNode->v_file_map, pos, bytesLeft, fileVecs, &fileVecCount, 0); 966 if (status != B_OK && status != B_BUFFER_OVERFLOW) 967 break; 968 969 bufferOverflow = status == B_BUFFER_OVERFLOW; 970 971 status = read_file_io_vec_pages(fatVolume->pm_dev->si_fd, fileVecs, fileVecCount, vecs, 972 count, &vecIndex, &vecOffset, &bytes); 973 if (status != B_OK || !bufferOverflow) 974 break; 975 976 pos += bytes; 977 bytesLeft -= bytes; 978 } 979 980 RETURN_ERROR(status); 981 } 982 983 984 static status_t 985 dosfs_write_pages(fs_volume* volume, fs_vnode* vnode, void* cookie, off_t pos, const iovec* vecs, 986 size_t count, size_t* _numBytes) 987 { 988 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 989 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 990 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 991 992 uint32 vecIndex = 0; 993 size_t vecOffset = 0; 994 size_t bytesLeft = *_numBytes; 995 status_t status; 996 997 FUNCTION_START("%p\n", bsdNode); 998 999 if (bsdNode->v_cache == NULL) 1000 return B_BAD_VALUE; 1001 1002 ReadLocker locker(bsdNode->v_vnlock->haikuRW); 1003 1004 if (MOUNTED_READ_ONLY(fatVolume) != 0) 1005 return B_READ_ONLY_DEVICE; 1006 1007 while (true) { 1008 struct file_io_vec fileVecs[8]; 1009 size_t fileVecCount = 8; 1010 bool bufferOverflow; 1011 size_t bytes = bytesLeft; 1012 1013 status 1014 = file_map_translate(bsdNode->v_file_map, pos, bytesLeft, fileVecs, &fileVecCount, 0); 1015 if (status != B_OK && status != B_BUFFER_OVERFLOW) 1016 break; 1017 1018 bufferOverflow = status == B_BUFFER_OVERFLOW; 1019 1020 status = write_file_io_vec_pages(fatVolume->pm_dev->si_fd, fileVecs, fileVecCount, vecs, 1021 count, &vecIndex, &vecOffset, &bytes); 1022 if (status != B_OK || !bufferOverflow) 1023 break; 1024 1025 pos += bytes; 1026 bytesLeft -= bytes; 1027 } 1028 1029 RETURN_ERROR(status); 1030 } 1031 1032 1033 static status_t 1034 dosfs_io(fs_volume* volume, fs_vnode* vnode, void* cookie, io_request* request) 1035 { 1036 #if KDEBUG_RW_LOCK_DEBUG 1037 // dosfs_io depends on read-locks being implicitly transferrable across threads. 1038 return B_UNSUPPORTED; 1039 #endif 1040 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 1041 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 1042 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 1043 1044 #ifndef FS_SHELL 1045 if (io_request_is_write(request) && MOUNTED_READ_ONLY(fatVolume) != 0) { 1046 notify_io_request(request, B_READ_ONLY_DEVICE); 1047 return B_READ_ONLY_DEVICE; 1048 } 1049 #endif 1050 1051 if (bsdNode->v_cache == NULL) { 1052 #ifndef FS_SHELL 1053 notify_io_request(request, B_BAD_VALUE); 1054 #endif 1055 panic("dosfs_io: no file cache\n"); 1056 RETURN_ERROR(B_BAD_VALUE); 1057 } 1058 1059 // divert to synchronous IO? 1060 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0 || bsdNode->v_sync == true) 1061 return B_UNSUPPORTED; 1062 1063 rw_lock_read_lock(&bsdNode->v_vnlock->haikuRW); 1064 1065 RETURN_ERROR(do_iterative_fd_io(fatVolume->pm_dev->si_fd, request, iterative_io_get_vecs_hook, 1066 iterative_io_finished_hook, bsdNode)); 1067 } 1068 1069 1070 static status_t 1071 dosfs_get_file_map(fs_volume* volume, fs_vnode* vnode, off_t position, size_t length, 1072 struct file_io_vec* vecs, size_t* _count) 1073 { 1074 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 1075 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 1076 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 1077 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data); 1078 1079 FUNCTION_START("%" B_PRIuSIZE " bytes at %" B_PRIdOFF " (vnode id %" B_PRIdINO " at %p)\n", 1080 length, position, fatNode->de_inode, bsdNode); 1081 1082 size_t max = *_count; 1083 *_count = 0; 1084 1085 if ((bsdNode->v_type & VDIR) != 0) 1086 return B_IS_A_DIRECTORY; 1087 1088 if (position < 0) 1089 position = 0; 1090 1091 size_t fileSize = fatNode->de_FileSize; 1092 1093 if (fileSize == 0 || length == 0 || static_cast<u_long>(position) >= fileSize) 1094 return B_OK; 1095 1096 // truncate to file size, taking overflow into account 1097 if (static_cast<uint64>(position + length) >= fileSize 1098 || static_cast<off_t>(position + length) < position) { 1099 length = fileSize - position; 1100 } 1101 1102 csi iter; 1103 status_t status = init_csi(fatVolume, fatNode->de_StartCluster, 0, &iter); 1104 if (status != B_OK) 1105 RETURN_ERROR(B_IO_ERROR); 1106 1107 size_t bytesPerSector = fatVolume->pm_BytesPerSec; 1108 1109 // file-relative sector in which position lies 1110 uint32 positionSector = position / bytesPerSector; 1111 1112 if (positionSector > 0) { 1113 status = iter_csi(&iter, positionSector); 1114 if (status != B_OK) 1115 RETURN_ERROR(status); 1116 } 1117 1118 status = validate_cs(iter.fatVolume, iter.cluster, iter.sector); 1119 if (status != B_OK) 1120 RETURN_ERROR(status); 1121 1122 int32 sectorOffset = position % bytesPerSector; 1123 size_t index = 0; 1124 1125 // Each iteration populates one vec 1126 while (length > 0) { 1127 off_t initFsSector = fs_sector(&iter); 1128 uint32 sectors = 1; 1129 1130 length -= min_c(length, bytesPerSector - sectorOffset); 1131 1132 // Each iteration advances iter to the next sector of the file. 1133 // Break when iter reaches the first sector of a non-contiguous cluster. 1134 while (length > 0) { 1135 status = iter_csi(&iter, 1); 1136 ASSERT(status == B_OK); 1137 status = validate_cs(iter.fatVolume, iter.cluster, iter.sector); 1138 if (status != B_OK) 1139 RETURN_ERROR(status); 1140 1141 if (initFsSector + sectors != fs_sector(&iter)) { 1142 // disjoint sectors, need to flush and begin a new vector 1143 break; 1144 } 1145 1146 length -= min_c(length, bytesPerSector); 1147 sectors++; 1148 } 1149 1150 vecs[index].offset = initFsSector * bytesPerSector + sectorOffset; 1151 vecs[index].length = sectors * bytesPerSector - sectorOffset; 1152 position += vecs[index].length; 1153 1154 // for the last vector only, extend to the end of the last cluster 1155 if (length == 0) { 1156 if (IS_FIXED_ROOT(fatNode) == 0) { 1157 uint32 remainder = position % fatVolume->pm_bpcluster; 1158 if (remainder != 0) 1159 vecs[index].length += (fatVolume->pm_bpcluster - remainder); 1160 } 1161 } 1162 1163 index++; 1164 1165 if (index >= max) { 1166 // we're out of file_io_vecs; let's bail out 1167 *_count = index; 1168 return B_BUFFER_OVERFLOW; 1169 } 1170 1171 sectorOffset = 0; 1172 } 1173 1174 *_count = index; 1175 1176 return B_OK; 1177 } 1178 1179 1180 static status_t 1181 dosfs_fsync(fs_volume* volume, fs_vnode* vnode) 1182 { 1183 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 1184 1185 FUNCTION_START("%p\n", bsdNode); 1186 1187 return _dosfs_fsync(bsdNode); 1188 } 1189 1190 1191 static status_t 1192 _dosfs_fsync(struct vnode* bsdNode) 1193 { 1194 mount* bsdVolume = bsdNode->v_mount; 1195 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 1196 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data); 1197 1198 ReadLocker locker(bsdNode->v_vnlock->haikuRW); 1199 1200 status_t status = B_OK; 1201 if (bsdNode->v_cache != NULL) { 1202 PRINT("fsync: file_cache_sync\n"); 1203 status = file_cache_sync(bsdNode->v_cache); 1204 } else { 1205 status = sync_clusters(bsdNode); 1206 } 1207 1208 // If user chose op sync mode, flush the whole block cache. This will ensure that 1209 // the metadata that is external to the direntry (FAT chain for this file and all directory 1210 // files in the hierarchy above this file) is also synced. If not, just sync the FAT and the 1211 // node's direntry, if it has one (the root directory doesn't). 1212 status_t externStatus = B_OK; 1213 1214 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0) { 1215 externStatus = block_cache_sync(bsdVolume->mnt_cache); 1216 if (externStatus != B_OK) 1217 REPORT_ERROR(externStatus); 1218 } else { 1219 size_t fatBlocks = (fatVolume->pm_fatsize * fatVolume->pm_FATs) / DEV_BSIZE; 1220 status_t fatStatus 1221 = block_cache_sync_etc(bsdVolume->mnt_cache, fatVolume->pm_fatblk, fatBlocks); 1222 if (fatStatus != B_OK) { 1223 externStatus = fatStatus; 1224 REPORT_ERROR(fatStatus); 1225 } 1226 if ((bsdNode->v_vflag & VV_ROOT) == 0) { 1227 status_t entryStatus = B_FROM_POSIX_ERROR(deupdat(fatNode, 1)); 1228 if (entryStatus != B_OK) { 1229 externStatus = entryStatus; 1230 REPORT_ERROR(entryStatus); 1231 } 1232 } 1233 } 1234 1235 if (status == B_OK) 1236 status = externStatus; 1237 1238 RETURN_ERROR(status); 1239 } 1240 1241 1242 static status_t 1243 dosfs_link(fs_volume* volume, fs_vnode* dir, const char* name, fs_vnode* vnode) 1244 { 1245 FUNCTION_START("attempt to assign %s to %p in directory %p\n", name, vnode, dir); 1246 1247 return B_UNSUPPORTED; 1248 } 1249 1250 1251 static status_t 1252 dosfs_unlink(fs_volume* volume, fs_vnode* dir, const char* name) 1253 { 1254 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 1255 vnode* bsdDir = reinterpret_cast<vnode*>(dir->private_node); 1256 denode* fatDir = reinterpret_cast<denode*>(bsdDir->v_data); 1257 vnode* bsdNode = NULL; 1258 denode* fatNode = NULL; 1259 1260 FUNCTION_START("%s in directory @ %p\n", name, bsdDir); 1261 1262 if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) 1263 return B_NOT_ALLOWED; 1264 1265 ComponentName bsdName(ISLASTCN, NOCRED, DELETE, 0, name); 1266 1267 // multiple unlinks of files in the same dir would interfere when msdosfs_lookup_ino sets 1268 // de_fndofset and de_fndcnt of the parent node 1269 WriteLocker dirLocker(bsdDir->v_vnlock->haikuRW); 1270 1271 // set bsdNode to the file to be removed 1272 daddr_t cluster; 1273 u_long offset; 1274 status_t status 1275 = B_FROM_POSIX_ERROR(msdosfs_lookup_ino(bsdDir, NULL, bsdName.Data(), &cluster, &offset)); 1276 if (status != B_OK) 1277 RETURN_ERROR(status); 1278 status = assign_inode_and_get(bsdVolume, cluster, offset, &bsdNode); 1279 if (status != B_OK) 1280 RETURN_ERROR(status); 1281 WriteLocker nodeLocker(bsdNode->v_vnlock->haikuRW); 1282 NodePutter nodePutter(bsdNode); 1283 fatNode = reinterpret_cast<denode*>(bsdNode->v_data); 1284 1285 if (bsdNode->v_type == VDIR) 1286 return B_IS_A_DIRECTORY; 1287 1288 status = _dosfs_access(bsdVolume, bsdNode, W_OK); 1289 if (status != B_OK) 1290 RETURN_ERROR(B_NOT_ALLOWED); 1291 1292 status = B_FROM_POSIX_ERROR(removede(fatDir, fatNode)); 1293 if (status != B_OK) 1294 RETURN_ERROR(status); 1295 1296 // Set the loc to a unique value. This effectively removes it from the 1297 // vcache without releasing its vnid for reuse. It also nicely reserves 1298 // the vnid from use by other nodes. This is okay because the vnode is 1299 // locked in memory after this point and loc will not be referenced from 1300 // here on. 1301 ino_t ino = fatNode->de_inode; 1302 status = vcache_set_entry(bsdVolume, ino, generate_unique_vnid(bsdVolume)); 1303 if (status != B_OK) 1304 RETURN_ERROR(status); 1305 1306 status = remove_vnode(volume, ino); 1307 if (status != B_OK) 1308 RETURN_ERROR(status); 1309 1310 status = entry_cache_remove(volume->id, fatDir->de_inode, name); 1311 if (status != B_OK) 1312 REPORT_ERROR(status); 1313 1314 notify_entry_removed(volume->id, fatDir->de_inode, name, ino); 1315 1316 nodeLocker.Unlock(); 1317 1318 if (status == B_OK && (bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0) { 1319 // sync the parent directory changes 1320 _dosfs_sync(bsdVolume, false); 1321 } 1322 1323 RETURN_ERROR(status); 1324 } 1325 1326 1327 /*! 1328 What follows is the basic algorithm: 1329 1330 if (file move) { 1331 if (dest file exists) 1332 remove dest file 1333 if (dest and src in same directory) { 1334 rewrite name in existing directory slot 1335 } else { 1336 write new entry in dest directory 1337 update offset and dirclust in denode 1338 clear old directory entry 1339 } 1340 } else { 1341 directory move 1342 if (dest directory exists) { 1343 if (dest is not empty) 1344 return ENOTEMPTY 1345 remove dest directory 1346 } 1347 if (dest and src in same directory) 1348 rewrite name in existing entry 1349 else { 1350 be sure dest is not a child of src directory 1351 write entry in dest directory 1352 update "." and ".." in moved directory 1353 clear old directory entry for moved directory 1354 } 1355 } 1356 */ 1357 status_t 1358 dosfs_rename(fs_volume* volume, fs_vnode* fromDir, const char* fromName, fs_vnode* toDir, 1359 const char* toName) 1360 { 1361 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 1362 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 1363 vnode* fromDirBsdNode = reinterpret_cast<vnode*>(fromDir->private_node); 1364 vnode* toDirBsdNode = reinterpret_cast<vnode*>(toDir->private_node); 1365 1366 if (fromDir == toDir && !strcmp(fromName, toName)) 1367 return B_OK; 1368 1369 if (is_filename_legal(toName) == false) { 1370 INFORM("file name '%s' is not permitted in the FAT filesystem\n", toName); 1371 return B_BAD_VALUE; 1372 } 1373 1374 ComponentName fromBsdName(ISLASTCN, NOCRED, RENAME, 0, fromName); 1375 ComponentName toBsdName(ISLASTCN, NOCRED, RENAME, 0, toName); 1376 1377 // Don't do 2 renames at the same time on the same volume. If moving to a new directory, 1378 // and the destination directory of one thread is the origin directory of the other, 1379 // and vice versa, a deadlock can occur. 1380 MutexLocker volumeLocker(bsdVolume->mnt_mtx.haikuMutex); 1381 1382 WriteLocker fromDirLocker(fromDirBsdNode->v_vnlock->haikuRW); 1383 WriteLocker toDirLocker; 1384 if (fromDirBsdNode != toDirBsdNode) 1385 toDirLocker.SetTo(toDirBsdNode->v_vnlock->haikuRW, false); 1386 1387 status_t status = _dosfs_access(bsdVolume, fromDirBsdNode, W_OK); 1388 if (status == B_OK && fromDirBsdNode != toDirBsdNode) 1389 status = _dosfs_access(bsdVolume, toDirBsdNode, W_OK); 1390 if (status != B_OK) 1391 RETURN_ERROR(status); 1392 1393 // get the 'from' node 1394 daddr_t fromCluster; 1395 u_long fromOffset; 1396 status = B_FROM_POSIX_ERROR( 1397 msdosfs_lookup_ino(fromDirBsdNode, NULL, fromBsdName.Data(), &fromCluster, &fromOffset)); 1398 if (status != B_OK) 1399 RETURN_ERROR(status); 1400 vnode* fromBsdNode; 1401 status = assign_inode_and_get(bsdVolume, fromCluster, fromOffset, &fromBsdNode); 1402 if (status != B_OK) 1403 RETURN_ERROR(status); 1404 NodePutter fromPutter(fromBsdNode); 1405 WriteLocker fromLocker(fromBsdNode->v_vnlock->haikuRW); 1406 1407 // make sure the from entry wasn't deleted before we locked it 1408 status = B_FROM_POSIX_ERROR( 1409 msdosfs_lookup_ino(fromDirBsdNode, NULL, fromBsdName.Data(), &fromCluster, &fromOffset)); 1410 if (status != B_OK) { 1411 INFORM("dosfs_rename: file no longer present\n"); 1412 RETURN_ERROR(status); 1413 } 1414 1415 // get the "to" node, if the target name already exists 1416 daddr_t toCluster; 1417 u_long toOffset; 1418 status = B_FROM_POSIX_ERROR( 1419 msdosfs_lookup_ino(toDirBsdNode, NULL, toBsdName.Data(), &toCluster, &toOffset)); 1420 if (status != B_OK && status != B_FROM_POSIX_ERROR(EJUSTRETURN)) 1421 RETURN_ERROR(status); 1422 vnode* toBsdNode = NULL; 1423 if (status == B_OK) { 1424 // the target name does exist 1425 status = assign_inode_and_get(bsdVolume, toCluster, toOffset, &toBsdNode); 1426 if (status != B_OK) 1427 RETURN_ERROR(status); 1428 } 1429 1430 // Is toName equivalent to fromName in the FAT filesystem? 1431 bool caseChange = false; 1432 if (fromBsdNode == toBsdNode) { 1433 // The names they must differ only in capitalization. Ignore the match that was found for 1434 // the "to" node. 1435 put_vnode(volume, reinterpret_cast<denode*>(toBsdNode->v_data)->de_inode); 1436 toBsdNode = NULL; 1437 caseChange = true; 1438 } 1439 1440 NodePutter toPutter; 1441 WriteLocker toLocker; 1442 1443 if (toBsdNode != NULL) { 1444 status = msdosfs_lookup_ino(toDirBsdNode, NULL, toBsdName.Data(), &toCluster, &toOffset); 1445 if (status != 0) { 1446 toBsdNode = NULL; 1447 status = B_OK; 1448 } else { 1449 toLocker.SetTo(toBsdNode->v_vnlock->haikuRW, false); 1450 toPutter.SetTo(toBsdNode); 1451 } 1452 } 1453 1454 denode* fromDirFatNode = reinterpret_cast<denode*>(fromDirBsdNode->v_data); 1455 denode* fromFatNode = reinterpret_cast<denode*>(fromBsdNode->v_data); 1456 denode* toDirFatNode = reinterpret_cast<denode*>(toDirBsdNode->v_data); 1457 denode* toFatNode = toBsdNode != NULL ? reinterpret_cast<denode*>(toBsdNode->v_data) : NULL; 1458 1459 PRINT("dosfs_rename: %" B_PRIu64 "/%s->%" B_PRIu64 "/%s\n", fromDirFatNode->de_inode, fromName, 1460 toDirFatNode->de_inode, toName); 1461 1462 u_long toDirOffset = toDirFatNode->de_fndoffset; 1463 1464 // Is fromName a directory? 1465 bool doingDirectory = false; 1466 // Be sure we are not renaming ".", "..", or an alias of ".". This leads to a 1467 // crippled directory tree. It's pretty tough to do a "ls" or "pwd" with the 1468 // "." directory entry missing, and "cd .."doesn't work if the ".." entry is missing. 1469 if ((fromFatNode->de_Attributes & ATTR_DIRECTORY) != 0) { 1470 // Avoid ".", "..", and aliases of "." for obvious reasons. 1471 if ((fromBsdName.Data()->cn_namelen == 1 && fromBsdName.Data()->cn_nameptr[0] == '.') 1472 || fromDirFatNode == fromFatNode || (fromBsdName.Data()->cn_flags & ISDOTDOT) != 0 1473 || (fromBsdName.Data()->cn_flags & ISDOTDOT) != 0) { 1474 RETURN_ERROR(B_BAD_VALUE); 1475 } 1476 doingDirectory = true; 1477 } 1478 1479 // Is the target being moved to new parent directory? 1480 bool newParent = fromDirFatNode != toDirFatNode ? true : false; 1481 1482 // If ".." must be changed (ie the directory gets a new parent) then the source 1483 // directory must not be in the directory hierarchy above the target, as this would 1484 // orphan everything below the source directory. Also the user must have write 1485 // permission in the source so as to be able to change "..". 1486 status = _dosfs_access(bsdVolume, fromBsdNode, W_OK); 1487 if (doingDirectory && newParent) { 1488 if (status != B_OK) // write access check above 1489 RETURN_ERROR(status); 1490 1491 rw_lock_write_lock(&fatVolume->pm_checkpath_lock.haikuRW); 1492 1493 // The BSD function doscheckpath requires a third argument to return the location of 1494 // any child directory of fromFatNode that is locked by another thread. In the port we 1495 // don't use make use of this information, we just wait for that node to be unlocked. 1496 daddr_t dummy; 1497 // Switch the 'to' directory from the WriteLocker to a simple lock. This is a workaround 1498 // for problems that occur when doscheckpath() works with the node lock, while that lock 1499 // is held by a WriteLocker. 1500 rw_lock_write_lock(&toDirBsdNode->v_vnlock->haikuRW); 1501 toDirLocker.Unlock(); 1502 status = B_FROM_POSIX_ERROR(doscheckpath(fromFatNode, toDirFatNode, &dummy)); 1503 toDirLocker.Lock(); 1504 rw_lock_write_unlock(&toDirBsdNode->v_vnlock->haikuRW); 1505 1506 rw_lock_write_unlock(&fatVolume->pm_checkpath_lock.haikuRW); 1507 if (status != B_OK) 1508 RETURN_ERROR(status); 1509 } 1510 1511 if (toFatNode != NULL) { 1512 // Target must be empty if a directory and have no links to it. Also, ensure source and 1513 // target are compatible (both directories, or both not directories). 1514 if ((toFatNode->de_Attributes & ATTR_DIRECTORY) != 0) { 1515 if (!dosdirempty(toFatNode)) 1516 RETURN_ERROR(B_DIRECTORY_NOT_EMPTY); 1517 if (!doingDirectory) 1518 RETURN_ERROR(B_NOT_A_DIRECTORY); 1519 entry_cache_remove(volume->id, toDirFatNode->de_inode, toBsdName.Data()->cn_nameptr); 1520 } else if (doingDirectory) { 1521 RETURN_ERROR(B_IS_A_DIRECTORY); 1522 } 1523 1524 // delete the file/directory that we are overwriting 1525 daddr_t remCluster; 1526 u_long remOffset; 1527 status = msdosfs_lookup_ino(toDirBsdNode, NULL, toBsdName.Data(), &remCluster, &remOffset); 1528 // set de_fndoffset for use by removede 1529 status = B_FROM_POSIX_ERROR(removede(toDirFatNode, toFatNode)); 1530 if (status != B_OK) 1531 RETURN_ERROR(status); 1532 1533 // Set the loc to a unique value. This effectively removes it from the vcache without 1534 // releasing its vnid for reuse. It also nicely reserves the vnid from use by other 1535 // nodes. This is okay because the vnode is locked in memory after this point and loc 1536 // will not be referenced from here on. 1537 vcache_set_entry(bsdVolume, toFatNode->de_inode, generate_unique_vnid(bsdVolume)); 1538 1539 entry_cache_remove(volume->id, toDirFatNode->de_inode, toName); 1540 notify_entry_removed(volume->id, toDirFatNode->de_inode, toName, toFatNode->de_inode); 1541 1542 remove_vnode(volume, toFatNode->de_inode); 1543 1544 toLocker.Unlock(); 1545 toPutter.Put(); 1546 1547 toBsdNode = NULL; 1548 toFatNode = NULL; 1549 } 1550 1551 // Convert the filename in toBsdName into a dos filename. We copy this into the denode and 1552 // directory entry for the destination file/directory. 1553 u_char toShortName[SHORTNAME_CSTRING], oldShortNameArray[SHORTNAME_LENGTH]; 1554 if (caseChange == false) { 1555 status = B_FROM_POSIX_ERROR(uniqdosname(toDirFatNode, toBsdName.Data(), toShortName)); 1556 if (status != B_OK) 1557 RETURN_ERROR(status); 1558 if (is_shortname_legal(toShortName) == false) 1559 return B_NOT_ALLOWED; 1560 } 1561 // if only changing case, the dos filename (always all-caps) will remain the same 1562 1563 // First write a new entry in the destination directory and mark the entry in the source 1564 // directory as deleted. If we moved a directory, then update its .. entry to point to 1565 // the new parent directory. 1566 if (caseChange == false) { 1567 memcpy(oldShortNameArray, fromFatNode->de_Name, SHORTNAME_LENGTH); 1568 memcpy(fromFatNode->de_Name, toShortName, SHORTNAME_LENGTH); // update denode 1569 } else { 1570 // We prefer to create the new dir entry before removing the old one, but if only 1571 // changing case, we remove the old dir entry first, so that msdosfs_lookup_ino call below 1572 // won't see it as a match for the to-name when it does its case-insensitive search, 1573 // which would cause it to return before it has found empty slots for the new dir entry. 1574 status = B_FROM_POSIX_ERROR(removede(fromDirFatNode, fromFatNode)); 1575 if (status != B_OK) { 1576 INFORM("rename removede error: %" B_PRIu64 "/%" B_PRIu64 ": %s\n", 1577 fromDirFatNode->de_inode, fromFatNode->de_inode, strerror(status)); 1578 msdosfs_integrity_error(fatVolume); 1579 RETURN_ERROR(status); 1580 } 1581 } 1582 1583 daddr_t createCluster; 1584 u_long createOffset; 1585 status 1586 = msdosfs_lookup_ino(toDirBsdNode, NULL, toBsdName.Data(), &createCluster, &createOffset); 1587 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW); 1588 // the FAT will be updated if the directory needs to be extended to hold another dirent 1589 if (status == EJUSTRETURN) { 1590 toDirFatNode->de_fndoffset = toDirOffset; 1591 // if the to-name already existed, ensure that creatde will write the new 1592 // direntry to the space previously occupied by the (removed) to-name entry 1593 status = createde(fromFatNode, toDirFatNode, NULL, toBsdName.Data()); 1594 } 1595 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW); 1596 if (status != B_OK) { 1597 if (caseChange == true) { 1598 // We failed to create the new dir entry, and the old dir entry is already gone. 1599 // Try to restore the old entry. Since the old name is a case variant of the new name 1600 // in the same directory, creating an entry with the old name will probably fail too. 1601 // Use the dos name instead of the long name, to simplify entry creation and try to 1602 // avoid the same mode of failure. 1603 ComponentName restoreName(ISLASTCN, NOCRED, CREATE, 0, 1604 reinterpret_cast<char*>(fromFatNode->de_Name)); 1605 createde(fromFatNode, fromDirFatNode, NULL, restoreName.Data()); 1606 } else { 1607 // we haven't removed the old dir entry yet 1608 memcpy(fromFatNode->de_Name, oldShortNameArray, SHORTNAME_LENGTH); 1609 } 1610 RETURN_ERROR(B_FROM_POSIX_ERROR(status)); 1611 } 1612 1613 // If fromFatNode is for a directory, then its name should always be "." since it is for the 1614 // directory entry in the directory itself (msdosfs_lookup() always translates to the "." 1615 // entry so as to get a unique denode, except for the root directory there are different 1616 // complications). However, we just corrupted its name to pass the correct name to 1617 // createde(). Undo this. 1618 if ((fromFatNode->de_Attributes & ATTR_DIRECTORY) != 0) 1619 memcpy(fromFatNode->de_Name, oldShortNameArray, SHORTNAME_LENGTH); 1620 fromFatNode->de_refcnt++; 1621 // offset the decrement that will occur in removede 1622 daddr_t remFromCluster; 1623 u_long remFromOffset; 1624 status = msdosfs_lookup_ino(fromDirBsdNode, NULL, fromBsdName.Data(), &remFromCluster, 1625 &remFromOffset); 1626 if (caseChange == false) { 1627 status = B_FROM_POSIX_ERROR(removede(fromDirFatNode, fromFatNode)); 1628 if (status != B_OK) { 1629 INFORM("rename removede error: %" B_PRIu64 "/%" B_PRIu64 ": %s\n", 1630 fromDirFatNode->de_inode, fromFatNode->de_inode, strerror(status)); 1631 msdosfs_integrity_error(fatVolume); 1632 RETURN_ERROR(status); 1633 } 1634 } 1635 if (!doingDirectory) { 1636 status = B_FROM_POSIX_ERROR(pcbmap(toDirFatNode, de_cluster(fatVolume, toDirOffset), 0, 1637 &fromFatNode->de_dirclust, 0)); 1638 if (status != B_OK) { 1639 msdosfs_integrity_error(fatVolume); 1640 // fs is corrupt 1641 RETURN_ERROR(status); 1642 } 1643 if (fromFatNode->de_dirclust == MSDOSFSROOT) 1644 fromFatNode->de_diroffset = toDirOffset; 1645 else 1646 fromFatNode->de_diroffset = toDirOffset & fatVolume->pm_crbomask; 1647 } 1648 1649 fromBsdNode->v_parent = toDirFatNode->de_inode; 1650 1651 ino_t newLocation = DETOI(fatVolume, fromFatNode->de_dirclust, fromFatNode->de_diroffset); 1652 vcache_set_entry(bsdVolume, fromFatNode->de_inode, newLocation); 1653 1654 // If we moved a directory to a new parent directory, then we must fixup the ".." entry in 1655 // the moved directory. 1656 if (doingDirectory && newParent) { 1657 buf* dotDotBuf = NULL; 1658 u_long clustNumber = fromFatNode->de_StartCluster; 1659 ASSERT(clustNumber != MSDOSFSROOT); 1660 // this should never happen 1661 daddr_t blockNumber = cntobn(fatVolume, clustNumber); 1662 status = B_FROM_POSIX_ERROR( 1663 bread(fatVolume->pm_devvp, blockNumber, fatVolume->pm_bpcluster, NOCRED, &dotDotBuf)); 1664 if (status != B_OK) { 1665 INFORM("rename read error: %" B_PRIu64 "/%" B_PRIu64 ": %s\n", 1666 fromDirFatNode->de_inode, fromFatNode->de_inode, strerror(status)); 1667 msdosfs_integrity_error(fatVolume); 1668 RETURN_ERROR(status); 1669 } 1670 direntry* dotDotEntry = reinterpret_cast<direntry*>(dotDotBuf->b_data) + 1; 1671 u_long parentClust = toDirFatNode->de_StartCluster; 1672 if (FAT32(fatVolume) == true && parentClust == fatVolume->pm_rootdirblk) 1673 parentClust = MSDOSFSROOT; 1674 putushort(dotDotEntry->deStartCluster, parentClust); 1675 if (FAT32(fatVolume) == true) 1676 putushort(dotDotEntry->deHighClust, parentClust >> 16); 1677 if (DOINGASYNC(fromBsdNode)) { 1678 bdwrite(dotDotBuf); 1679 } else if ((status = B_FROM_POSIX_ERROR(bwrite(dotDotBuf))) != B_OK) { 1680 INFORM("rename write error: %" B_PRIu64 "/%" B_PRIu64 ": %s\n", 1681 fromDirFatNode->de_inode, fromFatNode->de_inode, strerror(status)); 1682 msdosfs_integrity_error(fatVolume); 1683 RETURN_ERROR(status); 1684 } 1685 entry_cache_add(volume->id, fromFatNode->de_inode, "..", toDirFatNode->de_inode); 1686 } 1687 1688 status = entry_cache_remove(volume->id, fromDirFatNode->de_inode, fromName); 1689 if (status != B_OK) 1690 REPORT_ERROR(status); 1691 status = entry_cache_add(volume->id, toDirFatNode->de_inode, toName, fromFatNode->de_inode); 1692 if (status != B_OK) 1693 REPORT_ERROR(status); 1694 1695 status = notify_entry_moved(volume->id, fromDirFatNode->de_inode, fromName, 1696 toDirFatNode->de_inode, toName, fromFatNode->de_inode); 1697 if (status != B_OK) 1698 REPORT_ERROR(status); 1699 1700 set_mime_type(fromBsdNode, true); 1701 1702 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0) { 1703 // sync the directory entry changes 1704 status = block_cache_sync(bsdVolume->mnt_cache); 1705 } 1706 1707 RETURN_ERROR(status); 1708 } 1709 1710 1711 static status_t 1712 dosfs_access(fs_volume* vol, fs_vnode* node, int mode) 1713 { 1714 mount* bsdVolume = reinterpret_cast<mount*>(vol->private_volume); 1715 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(node->private_node); 1716 1717 ReadLocker locker(bsdNode->v_vnlock->haikuRW); 1718 1719 RETURN_ERROR(_dosfs_access(bsdVolume, bsdNode, mode)); 1720 } 1721 1722 1723 status_t 1724 _dosfs_access(const mount* bsdVolume, const struct vnode* bsdNode, const int mode) 1725 { 1726 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 1727 1728 if ((mode & W_OK) != 0 && MOUNTED_READ_ONLY(fatVolume)) 1729 RETURN_ERROR(B_READ_ONLY_DEVICE); 1730 1731 mode_t fileMode = 0; 1732 mode_bits(bsdNode, &fileMode); 1733 1734 // userlandfs does not provide check_access_permissions 1735 #ifdef USER 1736 return check_access_permissions_internal(mode, fileMode, fatVolume->pm_gid, fatVolume->pm_uid); 1737 #else 1738 return check_access_permissions(mode, fileMode, fatVolume->pm_gid, fatVolume->pm_uid); 1739 #endif 1740 } 1741 1742 1743 static status_t 1744 dosfs_rstat(fs_volume* volume, fs_vnode* vnode, struct stat* stat) 1745 { 1746 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 1747 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 1748 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 1749 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data); 1750 1751 ReadLocker locker(bsdNode->v_vnlock->haikuRW); 1752 1753 // file mode bits 1754 mode_bits(bsdNode, &stat->st_mode); 1755 // file type bits 1756 status_t status = B_OK; 1757 if (bsdNode->v_type == VDIR) 1758 stat->st_mode |= S_IFDIR; 1759 else if (bsdNode->v_type == VREG) 1760 stat->st_mode |= S_IFREG; 1761 else 1762 status = B_BAD_VALUE; 1763 1764 stat->st_nlink = 1; 1765 1766 // The FAT filesystem does not keep track of ownership at the file level 1767 stat->st_uid = fatVolume->pm_uid; 1768 1769 stat->st_gid = fatVolume->pm_gid; 1770 1771 stat->st_size = fatNode->de_FileSize; 1772 1773 stat->st_blksize = FAT_IO_SIZE; 1774 1775 fattime2timespec(fatNode->de_MDate, fatNode->de_MTime, 0, 1, &stat->st_mtim); 1776 1777 // FAT does not keep a record of last change time 1778 stat->st_ctim = stat->st_mtim; 1779 1780 fattime2timespec(fatNode->de_ADate, 0, 0, 1, &stat->st_atim); 1781 1782 fattime2timespec(fatNode->de_CDate, fatNode->de_CTime, fatNode->de_CHun, 1, &stat->st_crtim); 1783 1784 stat->st_blocks = howmany(fatNode->de_FileSize, 512); 1785 1786 RETURN_ERROR(status); 1787 } 1788 1789 1790 static status_t 1791 dosfs_wstat(fs_volume* volume, fs_vnode* vnode, const struct stat* stat, uint32 statMask) 1792 { 1793 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 1794 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 1795 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 1796 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data); 1797 1798 FUNCTION_START("inode %" B_PRIu64 ", @ %p\n", fatNode->de_inode, bsdNode); 1799 1800 WriteLocker locker(bsdNode->v_vnlock->haikuRW); 1801 1802 bool hasWriteAccess = _dosfs_access(bsdVolume, bsdNode, W_OK) == B_OK; 1803 uid_t uid = geteuid(); 1804 bool isOwnerOrRoot = uid == 0 || uid == fatVolume->pm_uid; 1805 ; 1806 1807 // We don't allow setting attributes on the root directory. The special case for the root 1808 // directory is because before FAT32, the root directory didn't have an entry for itself 1809 // (and was otherwise special). With FAT32, the root directory is not so special, but still 1810 // doesn't have an entry for itself. 1811 if (bsdNode->v_vflag & VV_ROOT) 1812 RETURN_ERROR(B_BAD_VALUE); 1813 1814 off_t previousSize = fatNode->de_FileSize; 1815 status_t status = B_OK; 1816 1817 if ((statMask & B_STAT_SIZE) != 0) { 1818 if (!hasWriteAccess) 1819 RETURN_ERROR(B_NOT_ALLOWED); 1820 1821 switch (bsdNode->v_type) { 1822 case VDIR: 1823 return B_IS_A_DIRECTORY; 1824 case VREG: 1825 break; 1826 default: 1827 return B_BAD_VALUE; 1828 break; 1829 } 1830 1831 if (stat->st_size >= MSDOSFS_FILESIZE_MAX) 1832 RETURN_ERROR(B_FILE_TOO_LARGE); 1833 1834 bool shrinking = previousSize > stat->st_size; 1835 1836 // If growing the file, detrunc will call deextend, which tries to zero out the new 1837 // clusters. We use the v_resizing flag to disable writes during detrunc to prevent that, 1838 // because using file_cache_write while the node is locked can cause a deadlock. 1839 // The new clusters will be cleared after return from detrunc instead. 1840 // We also disable writes in the case of shrinking the file because, unlike the detrunc 1841 // call in create or open, which always truncate to zero, this call will most likely pass 1842 // a size that is not a multiple of cluster size, so detrunc will want to zero out the end 1843 // of the last cluster. 1844 bsdNode->v_resizing = true; 1845 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW); 1846 status = B_FROM_POSIX_ERROR(detrunc(fatNode, stat->st_size, 0, NOCRED)); 1847 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW); 1848 bsdNode->v_resizing = false; 1849 if (status != B_OK) 1850 RETURN_ERROR(status); 1851 1852 PRINT("dosfs_wstat: inode %" B_PRIu64 ", @ %p size change from %" B_PRIdOFF " to %" B_PRIu64 1853 "\n", fatNode->de_inode, bsdNode, previousSize, stat->st_size); 1854 1855 locker.Unlock(); 1856 // avoid deadlock with dosfs_io 1857 file_cache_set_size(bsdNode->v_cache, fatNode->de_FileSize); 1858 if (shrinking == false && (statMask & B_STAT_SIZE_INSECURE) == 0) { 1859 status = fill_gap_with_zeros(bsdNode, previousSize, fatNode->de_FileSize); 1860 if (status != B_OK) 1861 RETURN_ERROR(status); 1862 } 1863 locker.Lock(); 1864 1865 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0) 1866 _dosfs_fsync(bsdNode); 1867 1868 fatNode->de_Attributes |= ATTR_ARCHIVE; 1869 fatNode->de_flag |= DE_MODIFIED; 1870 } 1871 1872 // DOS files only have the ability to have their writability attribute set, so we use the 1873 // owner write bit to set the readonly attribute. 1874 if ((statMask & B_STAT_MODE) != 0) { 1875 if (!isOwnerOrRoot) 1876 RETURN_ERROR(B_NOT_ALLOWED); 1877 PRINT("setting file mode to %o\n", stat->st_mode); 1878 if (bsdNode->v_type != VDIR) { 1879 if ((stat->st_mode & S_IWUSR) == 0) 1880 fatNode->de_Attributes |= ATTR_READONLY; 1881 else 1882 fatNode->de_Attributes &= ~ATTR_READONLY; 1883 1884 // We don't set the archive bit when modifying the time of 1885 // a directory to emulate the Windows/DOS behavior. 1886 fatNode->de_Attributes |= ATTR_ARCHIVE; 1887 fatNode->de_flag |= DE_MODIFIED; 1888 } 1889 } 1890 1891 if ((statMask & B_STAT_UID) != 0) { 1892 PRINT("cannot set UID at file level\n"); 1893 if (stat->st_uid != fatVolume->pm_uid) 1894 status = B_BAD_VALUE; 1895 } 1896 1897 if ((statMask & B_STAT_GID) != 0) { 1898 PRINT("cannot set GID at file level\n"); 1899 if (stat->st_gid != fatVolume->pm_gid) 1900 status = B_BAD_VALUE; 1901 } 1902 1903 if ((statMask & B_STAT_ACCESS_TIME) != 0) { 1904 PRINT("setting access time\n"); 1905 fatNode->de_flag &= ~DE_ACCESS; 1906 struct timespec atimGMT; 1907 local_to_GMT(&stat->st_atim, &atimGMT); 1908 timespec2fattime(&atimGMT, 0, &fatNode->de_ADate, NULL, NULL); 1909 if (bsdNode->v_type != VDIR) 1910 fatNode->de_Attributes |= ATTR_ARCHIVE; 1911 fatNode->de_flag |= DE_MODIFIED; 1912 } 1913 1914 if ((statMask & B_STAT_MODIFICATION_TIME) != 0) { 1915 // the user or root can do that or any user with write access 1916 if (!isOwnerOrRoot && !hasWriteAccess) 1917 RETURN_ERROR(B_NOT_ALLOWED); 1918 PRINT("setting modification time\n"); 1919 fatNode->de_flag &= ~DE_UPDATE; 1920 struct timespec mtimGMT; 1921 local_to_GMT(&stat->st_mtim, &mtimGMT); 1922 timespec2fattime(&mtimGMT, 0, &fatNode->de_MDate, &fatNode->de_MTime, NULL); 1923 if (bsdNode->v_type != VDIR) 1924 fatNode->de_Attributes |= ATTR_ARCHIVE; 1925 fatNode->de_flag |= DE_MODIFIED; 1926 } 1927 1928 if ((statMask & B_STAT_CREATION_TIME) != 0) { 1929 // the user or root can do that or any user with write access 1930 if (!isOwnerOrRoot && !hasWriteAccess) 1931 RETURN_ERROR(B_NOT_ALLOWED); 1932 PRINT("setting creation time\n"); 1933 struct timespec crtimGMT; 1934 local_to_GMT(&stat->st_crtim, &crtimGMT); 1935 timespec2fattime(&crtimGMT, 0, &fatNode->de_CDate, &fatNode->de_CTime, NULL); 1936 fatNode->de_flag |= DE_MODIFIED; 1937 } 1938 1939 // node change time is not recorded in the FAT file system 1940 1941 status = B_FROM_POSIX_ERROR(deupdat(fatNode, (bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0)); 1942 1943 notify_stat_changed(volume->id, bsdNode->v_parent, fatNode->de_inode, statMask); 1944 1945 RETURN_ERROR(status); 1946 } 1947 1948 1949 static status_t 1950 dosfs_create(fs_volume* volume, fs_vnode* dir, const char* name, int openMode, int perms, 1951 void** _cookie, ino_t* _newVnodeID) 1952 { 1953 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 1954 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 1955 vnode* bsdDir = reinterpret_cast<vnode*>(dir->private_node); 1956 denode* fatDir = reinterpret_cast<denode*>(bsdDir->v_data); 1957 1958 FUNCTION_START("create %s in %" B_PRIu64 ", perms = %o openMode =%o\n", name, fatDir->de_inode, 1959 perms, openMode); 1960 1961 ComponentName bsdName(ISLASTCN | MAKEENTRY, NOCRED, CREATE, 0, name); 1962 1963 WriteLocker locker(bsdDir->v_vnlock->haikuRW); 1964 1965 if (_dosfs_access(bsdVolume, bsdDir, open_mode_to_access(openMode)) != B_OK) 1966 RETURN_ERROR(B_NOT_ALLOWED); 1967 1968 if ((openMode & O_NOCACHE) != 0) 1969 RETURN_ERROR(B_UNSUPPORTED); 1970 1971 if (is_filename_legal(name) != true) { 1972 INFORM("invalid FAT file name '%s'\n", name); 1973 RETURN_ERROR(B_UNSUPPORTED); 1974 } 1975 1976 bool removed = false; 1977 status_t status = get_vnode_removed(volume, fatDir->de_inode, &removed); 1978 if (status == B_OK && removed == true) 1979 RETURN_ERROR(B_ENTRY_NOT_FOUND); 1980 1981 if ((openMode & O_RWMASK) == O_RDONLY) 1982 RETURN_ERROR(B_NOT_ALLOWED); 1983 1984 FileCookie* cookie = new(std::nothrow) FileCookie; 1985 if (cookie == NULL) 1986 RETURN_ERROR(B_NO_MEMORY); 1987 ObjectDeleter<FileCookie> cookieDeleter(cookie); 1988 1989 // In addition to checking for an existing file with this name, msdosfs_lookup_ino 1990 // will set de_fndoffset of the parent node to a vacant direntry slot if there is 1991 // no existing file, in preparation for createde. 1992 daddr_t cluster; 1993 u_long offset; 1994 status 1995 = B_FROM_POSIX_ERROR(msdosfs_lookup_ino(bsdDir, NULL, bsdName.Data(), &cluster, &offset)); 1996 1997 if (status == B_OK) { 1998 // there is already a file with this name 1999 vnode* existingBsdNode; 2000 status = assign_inode_and_get(bsdVolume, cluster, offset, &existingBsdNode); 2001 if (status != B_OK) 2002 RETURN_ERROR(status); 2003 WriteLocker existingLocker(existingBsdNode->v_vnlock->haikuRW); 2004 NodePutter existingPutter(existingBsdNode); 2005 denode* existingFatNode = reinterpret_cast<denode*>(existingBsdNode->v_data); 2006 2007 if ((openMode & O_EXCL) != 0) 2008 RETURN_ERROR(B_FILE_EXISTS); 2009 if (existingBsdNode->v_type == VDIR) 2010 RETURN_ERROR(B_NOT_ALLOWED); 2011 if ((openMode & O_TRUNC) != 0) { 2012 status = _dosfs_access(bsdVolume, existingBsdNode, open_mode_to_access(openMode)); 2013 if (status != B_OK) 2014 RETURN_ERROR(status); 2015 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW); 2016 status = B_FROM_POSIX_ERROR(detrunc(existingFatNode, 0, 0, NOCRED)); 2017 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW); 2018 if (status != B_OK) 2019 RETURN_ERROR(status); 2020 2021 existingLocker.Unlock(); 2022 // avoid deadlock that can happen when reducing cache size 2023 file_cache_set_size(existingBsdNode->v_cache, 0); 2024 2025 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0) 2026 _dosfs_fsync(existingBsdNode); 2027 } else { 2028 status = _dosfs_access(bsdVolume, existingBsdNode, open_mode_to_access(openMode)); 2029 if (status != B_OK) 2030 RETURN_ERROR(status); 2031 } 2032 2033 *_newVnodeID = existingFatNode->de_inode; 2034 2035 cookie->fMode = openMode; 2036 cookie->fLastSize = existingFatNode->de_FileSize; 2037 cookie->fMtimeAtOpen = existingFatNode->de_MTime; 2038 cookie->fMdateAtOpen = existingFatNode->de_MDate; 2039 cookie->fLastNotification = 0; 2040 *_cookie = cookie; 2041 cookieDeleter.Detach(); 2042 2043 return B_OK; 2044 } 2045 2046 if (status != B_FROM_POSIX_ERROR(EJUSTRETURN)) 2047 return status; 2048 2049 // If this is the FAT12/16 root directory and there is no space left we can't do anything. 2050 // This is because the root directory can not change size. 2051 if (fatDir->de_StartCluster == MSDOSFSROOT && fatDir->de_fndoffset >= fatDir->de_FileSize) { 2052 INFORM("root directory is full and cannot be expanded\n"); 2053 return B_UNSUPPORTED; 2054 } 2055 2056 // set up a dummy node that will be converted into a direntry 2057 denode newDirentry; 2058 memset(&newDirentry, 0, sizeof(newDirentry)); 2059 status = B_FROM_POSIX_ERROR(uniqdosname(fatDir, bsdName.Data(), newDirentry.de_Name)); 2060 if (status != B_OK) 2061 return status; 2062 if (is_shortname_legal(newDirentry.de_Name) == false) { 2063 INFORM("invalid FAT short file name '%s'\n", name); 2064 RETURN_ERROR(B_UNSUPPORTED); 2065 } 2066 newDirentry.de_Attributes = ATTR_ARCHIVE; 2067 if ((perms & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0) 2068 newDirentry.de_Attributes |= ATTR_READONLY; 2069 newDirentry.de_LowerCase = 0; 2070 newDirentry.de_StartCluster = 0; 2071 newDirentry.de_FileSize = 0; 2072 newDirentry.de_pmp = fatDir->de_pmp; 2073 newDirentry.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; 2074 timespec timeSpec; 2075 vfs_timestamp(&timeSpec); 2076 DETIMES(&newDirentry, &timeSpec, &timeSpec, &timeSpec); 2077 2078 // write the direntry 2079 u_long fndoffset = fatDir->de_fndoffset; 2080 // remember this value, because fatDir->de_fndoffset is liable to change during createde 2081 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW); 2082 status = B_FROM_POSIX_ERROR(createde(&newDirentry, fatDir, NULL, bsdName.Data())); 2083 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW); 2084 if (status != B_OK) 2085 RETURN_ERROR(status); 2086 2087 // determine the inode number 2088 u_long newCluster; 2089 status = B_FROM_POSIX_ERROR( 2090 pcbmap(fatDir, de_cluster(fatVolume, fndoffset), NULL, &newCluster, NULL)); 2091 if (status != B_OK) 2092 RETURN_ERROR(status); 2093 uint32 newOffset = fndoffset; 2094 if (newCluster != MSDOSFSROOT) 2095 newOffset = fndoffset % fatVolume->pm_bpcluster; 2096 ino_t inode = DETOI(fatVolume, newCluster, newOffset); 2097 status = assign_inode(bsdVolume, &inode); 2098 if (status != B_OK) 2099 RETURN_ERROR(status); 2100 2101 // set up the actual node 2102 vnode* bsdNode; 2103 status = _dosfs_read_vnode(bsdVolume, inode, &bsdNode, false); 2104 if (status != B_OK) 2105 RETURN_ERROR(status); 2106 mode_t nodeType = 0; 2107 if (bsdNode->v_type == VDIR) 2108 nodeType = S_IFDIR; 2109 else if (bsdNode->v_type == VREG) 2110 nodeType = S_IFREG; 2111 else 2112 panic("dosfs_create: unknown node type\n"); 2113 2114 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data); 2115 2116 cookie->fMode = openMode; 2117 cookie->fLastSize = fatNode->de_FileSize; 2118 cookie->fMtimeAtOpen = fatNode->de_MTime; 2119 cookie->fMdateAtOpen = fatNode->de_MDate; 2120 cookie->fLastNotification = 0; 2121 *_cookie = cookie; 2122 2123 status = publish_vnode(volume, inode, bsdNode, &gFATVnodeOps, nodeType, 0); 2124 2125 // This is usually done in _dosfs_read_vnode. However, the node wasn't published yet, 2126 // so it would not have worked there. 2127 bsdNode->v_cache 2128 = file_cache_create(fatVolume->pm_dev->si_id, fatNode->de_inode, fatNode->de_FileSize); 2129 bsdNode->v_file_map 2130 = file_map_create(fatVolume->pm_dev->si_id, fatNode->de_inode, fatNode->de_FileSize); 2131 2132 ASSERT(static_cast<ino_t>(fatNode->de_inode) == inode); 2133 *_newVnodeID = fatNode->de_inode; 2134 2135 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0) 2136 _dosfs_fsync(bsdNode); 2137 2138 entry_cache_add(volume->id, fatDir->de_inode, name, fatNode->de_inode); 2139 notify_entry_created(volume->id, fatDir->de_inode, name, fatNode->de_inode); 2140 2141 cookieDeleter.Detach(); 2142 2143 return B_OK; 2144 } 2145 2146 2147 status_t 2148 dosfs_open(fs_volume* volume, fs_vnode* vnode, int openMode, void** _cookie) 2149 { 2150 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 2151 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 2152 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 2153 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data); 2154 2155 FUNCTION_START("node %" B_PRIu64 " @ %p, omode %o\n", fatNode->de_inode, bsdNode, openMode); 2156 2157 *_cookie = NULL; 2158 2159 if ((openMode & O_NOCACHE) != 0) 2160 RETURN_ERROR(B_UNSUPPORTED); 2161 2162 if ((openMode & O_CREAT) != 0) { 2163 PRINT("dosfs_open called with O_CREAT. call dosfs_create instead!\n"); 2164 return B_BAD_VALUE; 2165 } 2166 2167 WriteLocker locker(bsdNode->v_vnlock->haikuRW); 2168 2169 // Opening a directory read-only is allowed, although you can't read 2170 // any data from it. 2171 if (bsdNode->v_type == VDIR && (openMode & O_RWMASK) != O_RDONLY) 2172 return B_IS_A_DIRECTORY; 2173 if ((openMode & O_DIRECTORY) != 0 && bsdNode->v_type != VDIR) 2174 return B_NOT_A_DIRECTORY; 2175 2176 if ((bsdVolume->mnt_flag & MNT_RDONLY) != 0 || (fatNode->de_Attributes & ATTR_READONLY) != 0) 2177 openMode = (openMode & ~O_RWMASK) | O_RDONLY; 2178 2179 if ((openMode & O_TRUNC) != 0 && (openMode & O_RWMASK) == O_RDONLY) 2180 return B_NOT_ALLOWED; 2181 2182 status_t status = _dosfs_access(bsdVolume, bsdNode, open_mode_to_access(openMode)); 2183 if (status != B_OK) 2184 RETURN_ERROR(status); 2185 2186 FileCookie* cookie = new(std::nothrow) FileCookie; 2187 if (cookie == NULL) 2188 RETURN_ERROR(B_NO_MEMORY); 2189 ObjectDeleter<FileCookie> cookieDeleter(cookie); 2190 cookie->fMode = openMode; 2191 cookie->fLastSize = fatNode->de_FileSize; 2192 cookie->fMtimeAtOpen = fatNode->de_MTime; 2193 cookie->fMdateAtOpen = fatNode->de_MDate; 2194 cookie->fLastNotification = 0; 2195 *_cookie = cookie; 2196 2197 if ((openMode & O_TRUNC) != 0) { 2198 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW); 2199 status = B_FROM_POSIX_ERROR(detrunc(fatNode, 0, 0, NOCRED)); 2200 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW); 2201 if (status != B_OK) 2202 RETURN_ERROR(status); 2203 locker.Unlock(); 2204 status = file_cache_set_size(bsdNode->v_cache, 0); 2205 if (status != B_OK) 2206 RETURN_ERROR(status); 2207 } 2208 2209 cookieDeleter.Detach(); 2210 2211 return B_OK; 2212 } 2213 2214 2215 static status_t 2216 dosfs_close(fs_volume* volume, fs_vnode* vnode, void* cookie) 2217 { 2218 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 2219 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data); 2220 2221 FUNCTION_START("%s (inode %" B_PRIu64 " at %p)\n", fatNode->de_Name, fatNode->de_inode, 2222 bsdNode); 2223 2224 WriteLocker locker(bsdNode->v_vnlock->haikuRW); 2225 2226 struct timespec timeSpec; 2227 vfs_timestamp(&timeSpec); 2228 DETIMES(fatNode, &timeSpec, &timeSpec, &timeSpec); 2229 2230 FileCookie* fatCookie = reinterpret_cast<FileCookie*>(cookie); 2231 bool changedSize = fatCookie->fLastSize != fatNode->de_FileSize ? true : false; 2232 bool changedTime = false; 2233 if (fatCookie->fMtimeAtOpen != fatNode->de_MTime 2234 || fatCookie->fMdateAtOpen != fatNode->de_MDate) { 2235 changedTime = true; 2236 } 2237 if (changedSize || changedTime) { 2238 notify_stat_changed(volume->id, bsdNode->v_parent, fatNode->de_inode, 2239 (changedTime ? B_STAT_MODIFICATION_TIME : 0) | (changedSize ? B_STAT_SIZE : 0)); 2240 } 2241 2242 if ((bsdNode->v_mount->mnt_flag & MNT_SYNCHRONOUS) != 0) 2243 deupdat(fatNode, 1); 2244 2245 return B_OK; 2246 } 2247 2248 2249 static status_t 2250 dosfs_free_cookie(fs_volume* volume, fs_vnode* vnode, void* cookie) 2251 { 2252 FUNCTION_START("%p\n", vnode->private_node); 2253 2254 delete reinterpret_cast<FileCookie*>(cookie); 2255 2256 return B_OK; 2257 } 2258 2259 2260 status_t 2261 dosfs_read(fs_volume* volume, fs_vnode* vnode, void* cookie, off_t pos, void* buffer, 2262 size_t* length) 2263 { 2264 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 2265 2266 FileCookie* fatCookie = reinterpret_cast<FileCookie*>(cookie); 2267 2268 FUNCTION_START("%" B_PRIuSIZE " bytes at %" B_PRIdOFF " (node %" B_PRIu64 " @ %p)\n", *length, 2269 pos, reinterpret_cast<denode*>(bsdNode->v_data)->de_inode, bsdNode); 2270 2271 if ((bsdNode->v_type & VDIR) != 0) { 2272 *length = 0; 2273 return B_IS_A_DIRECTORY; 2274 } 2275 2276 if ((fatCookie->fMode & O_RWMASK) == O_WRONLY) { 2277 *length = 0; 2278 RETURN_ERROR(B_NOT_ALLOWED); 2279 } 2280 2281 // The userlandfs implementation of file_cache_read seems to rely on the FS to decide 2282 // when to stop reading - it returns B_BAD_VALUE if called again after EOF has been reached. 2283 #if USER 2284 if (static_cast<u_long>(pos) >= reinterpret_cast<denode*>(bsdNode->v_data)->de_FileSize) { 2285 *length = 0; 2286 return B_OK; 2287 } 2288 #endif 2289 2290 RETURN_ERROR(file_cache_read(bsdNode->v_cache, fatCookie, pos, buffer, length)); 2291 } 2292 2293 2294 status_t 2295 dosfs_write(fs_volume* volume, fs_vnode* vnode, void* cookie, off_t pos, const void* buffer, 2296 size_t* length) 2297 { 2298 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 2299 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 2300 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 2301 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data); 2302 2303 if (pos < 0) 2304 return B_BAD_VALUE; 2305 2306 FileCookie* fatCookie = reinterpret_cast<FileCookie*>(cookie); 2307 2308 if ((fatCookie->fMode & O_RWMASK) == O_RDONLY) 2309 RETURN_ERROR(B_NOT_ALLOWED); 2310 2311 WriteLocker locker(bsdNode->v_vnlock->haikuRW); 2312 2313 FUNCTION_START("%" B_PRIuSIZE " bytes at %" B_PRIdOFF " from buffer at %p (vnode id %" B_PRIu64 2314 ")\n", *length, pos, buffer, fatNode->de_inode); 2315 2316 size_t origSize = fatNode->de_FileSize; 2317 2318 switch (bsdNode->v_type) { 2319 case VREG: 2320 if ((fatCookie->fMode & O_APPEND) != 0) 2321 pos = fatNode->de_FileSize; 2322 break; 2323 case VDIR: 2324 return B_IS_A_DIRECTORY; 2325 default: 2326 RETURN_ERROR(B_BAD_VALUE); 2327 } 2328 2329 // if they've exceeded their filesize limit, tell them about it 2330 if (pos >= MSDOSFS_FILESIZE_MAX) 2331 RETURN_ERROR(B_FILE_TOO_LARGE); 2332 2333 if ((pos + *length) >= MSDOSFS_FILESIZE_MAX) 2334 *length = static_cast<size_t>(MSDOSFS_FILESIZE_MAX - pos); 2335 2336 // if we write beyond the end of the file, extend it 2337 status_t status = B_OK; 2338 if (pos + (*length) > fatNode->de_FileSize) { 2339 PRINT("dosfs_write: extending %" B_PRIu64 " to %" B_PRIdOFF " > file size %lu\n", 2340 fatNode->de_inode, pos + *length, fatNode->de_FileSize); 2341 2342 bsdNode->v_resizing = true; 2343 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW); 2344 status = B_FROM_POSIX_ERROR(deextend(fatNode, static_cast<size_t>(pos) + *length, NOCRED)); 2345 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW); 2346 bsdNode->v_resizing = false; 2347 // if there is not enough free space to extend as requested, we return here 2348 if (status != B_OK) 2349 RETURN_ERROR(status); 2350 2351 PRINT("setting file size to %lu (%lu clusters)\n", fatNode->de_FileSize, 2352 de_clcount(fatVolume, fatNode->de_FileSize)); 2353 ASSERT(fatNode->de_FileSize == static_cast<unsigned long>(pos) + *length); 2354 } 2355 2356 locker.Unlock(); 2357 status = file_cache_set_size(bsdNode->v_cache, fatNode->de_FileSize); 2358 if (status == B_OK) { 2359 status = file_cache_write(bsdNode->v_cache, fatCookie, pos, buffer, length); 2360 if (status != B_OK) { 2361 REPORT_ERROR(status); 2362 status = B_OK; 2363 } 2364 if (*length == 0) 2365 status = B_IO_ERROR; 2366 } 2367 if (status != B_OK) { 2368 // complete write failure 2369 if (origSize < fatNode->de_FileSize) { 2370 // return file to its previous size 2371 int truncFlag = ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0) ? IO_SYNC : 0; 2372 locker.Lock(); 2373 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW); 2374 status_t undoStatus = B_FROM_POSIX_ERROR(detrunc(fatNode, origSize, truncFlag, NOCRED)); 2375 if (undoStatus != 0) 2376 REPORT_ERROR(undoStatus); 2377 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW); 2378 locker.Unlock(); 2379 file_cache_set_size(bsdNode->v_cache, origSize); 2380 } 2381 RETURN_ERROR(status); 2382 } 2383 2384 // do the zeroing that is disabled in deextend 2385 if (static_cast<u_long>(pos) > origSize) { 2386 status = fill_gap_with_zeros(bsdNode, origSize, pos); 2387 if (status != B_OK) 2388 REPORT_ERROR(status); 2389 } 2390 2391 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0) { 2392 status = _dosfs_fsync(bsdNode); 2393 if (status != B_OK) 2394 REPORT_ERROR(status); 2395 } 2396 2397 if (fatNode->de_FileSize > 0 && fatNode->de_FileSize > fatCookie->fLastSize 2398 && system_time() > fatCookie->fLastNotification + INODE_NOTIFICATION_INTERVAL) { 2399 notify_stat_changed(volume->id, bsdNode->v_parent, fatNode->de_inode, 2400 B_STAT_MODIFICATION_TIME | B_STAT_SIZE | B_STAT_INTERIM_UPDATE); 2401 fatCookie->fLastSize = fatNode->de_FileSize; 2402 fatCookie->fLastNotification = system_time(); 2403 } 2404 2405 return B_OK; 2406 } 2407 2408 2409 static status_t 2410 dosfs_mkdir(fs_volume* volume, fs_vnode* parent, const char* name, int perms) 2411 { 2412 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 2413 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 2414 vnode* bsdParent = reinterpret_cast<vnode*>(parent->private_node); 2415 denode* fatParent = reinterpret_cast<denode*>(bsdParent->v_data); 2416 2417 FUNCTION_START("%" B_PRIu64 "/%s (perm %o)\n", fatParent->de_inode, name, perms); 2418 2419 if (is_filename_legal(name) == false) 2420 RETURN_ERROR(B_BAD_VALUE); 2421 2422 ComponentName bsdName(ISLASTCN, NOCRED, CREATE, 0, name); 2423 2424 WriteLocker locker(bsdParent->v_vnlock->haikuRW); 2425 2426 status_t status = _dosfs_access(bsdVolume, bsdParent, W_OK); 2427 if (status != B_OK) 2428 RETURN_ERROR(status); 2429 2430 if (bsdParent->v_type != VDIR) 2431 return B_BAD_TYPE; 2432 2433 bool removed = false; 2434 status = get_vnode_removed(volume, fatParent->de_inode, &removed); 2435 if (status == B_OK && removed == true) 2436 RETURN_ERROR(B_ENTRY_NOT_FOUND); 2437 2438 // add file type information to perms 2439 perms &= ~S_IFMT; 2440 perms |= S_IFDIR; 2441 2442 // set fatParent::de_fndoffset and de_fndcnt in preparation for createde 2443 vnode* existingBsdNode; 2444 status = msdosfs_lookup_ino(bsdParent, &existingBsdNode, bsdName.Data(), NULL, NULL); 2445 if (status == 0) { 2446 // a directory with this name already exists 2447 rw_lock_write_unlock(&existingBsdNode->v_vnlock->haikuRW); 2448 put_vnode(volume, (reinterpret_cast<denode*>(existingBsdNode->v_data))->de_inode); 2449 return B_FILE_EXISTS; 2450 } 2451 if (status != EJUSTRETURN) 2452 RETURN_ERROR(B_FROM_POSIX_ERROR(status)); 2453 2454 // If this is the FAT12/16 root directory and there is no space left we can't do anything. 2455 // This is because the root directory can not change size. 2456 if (fatParent->de_StartCluster == MSDOSFSROOT 2457 && fatParent->de_fndoffset >= fatParent->de_FileSize) { 2458 INFORM("root directory is full and cannot be expanded\n"); 2459 return B_UNSUPPORTED; 2460 } 2461 2462 // allocate a cluster to hold the about to be created directory 2463 u_long newCluster; 2464 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW); 2465 status = B_FROM_POSIX_ERROR(clusteralloc(fatVolume, 0, 1, CLUST_EOFE, &newCluster, NULL)); 2466 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW); 2467 if (status != B_OK) 2468 RETURN_ERROR(status); 2469 2470 // start setting up a dummy node to convert to the new direntry 2471 denode newEntry; 2472 memset(&newEntry, 0, sizeof(newEntry)); 2473 newEntry.de_pmp = fatVolume; 2474 newEntry.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; 2475 timespec timeSpec; 2476 vfs_timestamp(&timeSpec); 2477 DETIMES(&newEntry, &timeSpec, &timeSpec, &timeSpec); 2478 2479 // Now fill the cluster with the "." and ".." entries. And write the cluster to disk. This 2480 // way it is there for the parent directory to be pointing at if there were a crash. 2481 int startBlock = cntobn(fatVolume, newCluster); 2482 buf* newData = getblk(fatVolume->pm_devvp, startBlock, fatVolume->pm_bpcluster, 0, 0, 0); 2483 if (newData == NULL) { 2484 clusterfree(fatVolume, newCluster); 2485 RETURN_ERROR(B_ERROR); 2486 } 2487 // clear out rest of cluster to keep scandisk happy 2488 memset(newData->b_data, 0, fatVolume->pm_bpcluster); 2489 memcpy(newData->b_data, &gDirTemplate, sizeof gDirTemplate); 2490 direntry* childEntries = reinterpret_cast<direntry*>(newData->b_data); 2491 putushort(childEntries[0].deStartCluster, newCluster); 2492 putushort(childEntries[0].deCDate, newEntry.de_CDate); 2493 putushort(childEntries[0].deCTime, newEntry.de_CTime); 2494 childEntries[0].deCHundredth = newEntry.de_CHun; 2495 putushort(childEntries[0].deADate, newEntry.de_ADate); 2496 putushort(childEntries[0].deMDate, newEntry.de_MDate); 2497 putushort(childEntries[0].deMTime, newEntry.de_MTime); 2498 u_long parentCluster = fatParent->de_StartCluster; 2499 // Although the root directory has a non-magic starting cluster number for FAT32, chkdsk and 2500 // fsck_msdosfs still require references to it in dotdot entries to be magic. 2501 if (FAT32(fatVolume) == true && parentCluster == fatVolume->pm_rootdirblk) 2502 parentCluster = MSDOSFSROOT; 2503 putushort(childEntries[1].deStartCluster, parentCluster); 2504 putushort(childEntries[1].deCDate, newEntry.de_CDate); 2505 putushort(childEntries[1].deCTime, newEntry.de_CTime); 2506 childEntries[1].deCHundredth = newEntry.de_CHun; 2507 putushort(childEntries[1].deADate, newEntry.de_ADate); 2508 putushort(childEntries[1].deMDate, newEntry.de_MDate); 2509 putushort(childEntries[1].deMTime, newEntry.de_MTime); 2510 if (FAT32(fatVolume) == true) { 2511 putushort(childEntries[0].deHighClust, newCluster >> 16); 2512 putushort(childEntries[1].deHighClust, parentCluster >> 16); 2513 } 2514 2515 if (DOINGASYNC(bsdParent) == true) { 2516 bdwrite(newData); 2517 } else if ((status = B_FROM_POSIX_ERROR(bwrite(newData))) != B_OK) { 2518 clusterfree(fatVolume, newCluster); 2519 RETURN_ERROR(status); 2520 } 2521 2522 // Now build up a directory entry pointing to the newly allocated cluster. This will be 2523 // written to an empty slot in the parent directory. 2524 status = B_FROM_POSIX_ERROR(uniqdosname(fatParent, bsdName.Data(), newEntry.de_Name)); 2525 if (status == B_OK && is_shortname_legal(newEntry.de_Name) == false) 2526 status = B_UNSUPPORTED; 2527 if (status != B_OK) { 2528 clusterfree(fatVolume, newCluster); 2529 RETURN_ERROR(status); 2530 } 2531 2532 newEntry.de_Attributes = ATTR_DIRECTORY; 2533 // The FAT on-disk direntry is limited in the permissions it can store 2534 if ((perms & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0) 2535 newEntry.de_Attributes |= ATTR_READONLY; 2536 newEntry.de_LowerCase = 0; 2537 newEntry.de_StartCluster = newCluster; 2538 newEntry.de_FileSize = 0; 2539 2540 // convert newEntry into a new direntry and write it 2541 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW); 2542 // lock FAT in case parent must be extended to hold another direntry 2543 status = B_FROM_POSIX_ERROR(createde(&newEntry, fatParent, NULL, bsdName.Data())); 2544 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW); 2545 if (status != B_OK) { 2546 clusterfree(fatVolume, newCluster); 2547 RETURN_ERROR(status); 2548 } 2549 2550 // set up the actual node 2551 ino_t inode = DETOI(fatVolume, newCluster, 0); 2552 assign_inode(bsdVolume, &inode); 2553 vnode* bsdNode; 2554 status = _dosfs_read_vnode(bsdVolume, inode, &bsdNode); 2555 if (status != B_OK) { 2556 clusterfree(fatVolume, newCluster); 2557 RETURN_ERROR(status); 2558 } 2559 // parent is not accessible in _dosfs_read_vnode when the node is a directory. 2560 bsdNode->v_parent = fatParent->de_inode; 2561 2562 status = publish_vnode(volume, inode, bsdNode, &gFATVnodeOps, S_IFDIR, 0); 2563 if (status != B_OK) { 2564 clusterfree(fatVolume, newCluster); 2565 RETURN_ERROR(status); 2566 } 2567 2568 put_vnode(volume, inode); 2569 2570 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0) 2571 _dosfs_fsync(bsdNode); 2572 2573 entry_cache_add(volume->id, fatParent->de_inode, name, inode); 2574 2575 notify_entry_created(volume->id, fatParent->de_inode, name, inode); 2576 2577 return B_OK; 2578 } 2579 2580 2581 static status_t 2582 dosfs_rmdir(fs_volume* volume, fs_vnode* parent, const char* name) 2583 { 2584 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 2585 vnode* bsdParent = reinterpret_cast<vnode*>(parent->private_node); 2586 denode* fatParent = reinterpret_cast<denode*>(bsdParent->v_data); 2587 2588 FUNCTION_START("%s in %" B_PRIu64 " at %p\n", name, fatParent->de_inode, bsdParent); 2589 2590 if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) 2591 return B_NOT_ALLOWED; 2592 2593 ComponentName bsdName(ISLASTCN, NOCRED, DELETE, 0, name); 2594 2595 WriteLocker parentLocker(bsdParent->v_vnlock->haikuRW); 2596 2597 daddr_t cluster; 2598 u_long offset; 2599 status_t status = B_FROM_POSIX_ERROR( 2600 msdosfs_lookup_ino(bsdParent, NULL, bsdName.Data(), &cluster, &offset)); 2601 if (status != B_OK) 2602 RETURN_ERROR(status); 2603 2604 vnode* bsdTarget; 2605 status = assign_inode_and_get(bsdVolume, cluster, offset, &bsdTarget); 2606 if (status != B_OK) 2607 RETURN_ERROR(status); 2608 WriteLocker targetLocker(bsdTarget->v_vnlock->haikuRW); 2609 NodePutter targetPutter(bsdTarget); 2610 denode* fatTarget = reinterpret_cast<denode*>(bsdTarget->v_data); 2611 2612 if (bsdTarget->v_type != VDIR) 2613 return B_NOT_A_DIRECTORY; 2614 if ((bsdTarget->v_vflag & VV_ROOT) != 0) 2615 return B_NOT_ALLOWED; 2616 if (dosdirempty(fatTarget) == false) 2617 return B_DIRECTORY_NOT_EMPTY; 2618 2619 status = _dosfs_access(bsdVolume, bsdTarget, W_OK); 2620 if (status != B_OK) 2621 RETURN_ERROR(status); 2622 2623 status = B_FROM_POSIX_ERROR(removede(fatParent, fatTarget)); 2624 if (status != B_OK) 2625 RETURN_ERROR(status); 2626 2627 // Set the loc to a unique value. This effectively removes it from the vcache without 2628 // releasing its vnid for reuse. It also nicely reserves the vnid from use by other nodes. 2629 // This is okay because the vnode is locked in memory after this point and loc will not 2630 // be referenced from here on. 2631 status = vcache_set_entry(bsdVolume, fatTarget->de_inode, generate_unique_vnid(bsdVolume)); 2632 if (status != B_OK) 2633 RETURN_ERROR(status); 2634 2635 status = remove_vnode(volume, fatTarget->de_inode); 2636 if (status != B_OK) 2637 RETURN_ERROR(status); 2638 2639 targetLocker.Unlock(); 2640 2641 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0) 2642 _dosfs_sync(bsdVolume, false); 2643 2644 entry_cache_remove(volume->id, fatTarget->de_inode, ".."); 2645 entry_cache_remove(volume->id, fatParent->de_inode, name); 2646 2647 notify_entry_removed(volume->id, fatParent->de_inode, name, fatTarget->de_inode); 2648 2649 return B_OK; 2650 } 2651 2652 2653 static status_t 2654 dosfs_opendir(fs_volume* volume, fs_vnode* vnode, void** _cookie) 2655 { 2656 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 2657 2658 FUNCTION_START("%p\n", bsdNode); 2659 2660 ReadLocker locker(bsdNode->v_vnlock->haikuRW); 2661 2662 *_cookie = NULL; 2663 2664 if ((bsdNode->v_type & VDIR) == 0) 2665 return B_NOT_A_DIRECTORY; 2666 2667 DirCookie* cookie = new(std::nothrow) DirCookie; 2668 if (cookie == NULL) 2669 RETURN_ERROR(B_NO_MEMORY); 2670 2671 cookie->fIndex = 0; 2672 2673 *_cookie = cookie; 2674 2675 return B_OK; 2676 } 2677 2678 2679 status_t 2680 dosfs_closedir(fs_volume* volume, fs_vnode* vnode, void* cookie) 2681 { 2682 FUNCTION_START("%p\n", vnode->private_node); 2683 2684 return B_OK; 2685 } 2686 2687 2688 status_t 2689 dosfs_free_dircookie(fs_volume* volume, fs_vnode* vnode, void* cookie) 2690 { 2691 delete reinterpret_cast<DirCookie*>(cookie); 2692 2693 return B_OK; 2694 } 2695 2696 2697 static status_t 2698 dosfs_readdir(fs_volume* volume, fs_vnode* vnode, void* cookie, struct dirent* buffer, 2699 size_t bufferSize, uint32* _num) 2700 { 2701 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 2702 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data); 2703 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 2704 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data); 2705 2706 FUNCTION_START("vp %p(%" B_PRIu64 "), bufferSize %lu, entries to be read %" B_PRIu32 "\n", 2707 bsdNode, fatNode->de_inode, bufferSize, *_num); 2708 2709 WriteLocker locker(bsdNode->v_vnlock->haikuRW); 2710 2711 if ((fatNode->de_Attributes & ATTR_DIRECTORY) == 0) 2712 RETURN_ERROR(B_NOT_A_DIRECTORY); 2713 2714 uint32 entriesRequested = *_num; 2715 *_num = 0; 2716 2717 // struct dirent is defined differently in Haiku and FreeBSD. In the ported driver, 2718 // Haiku's definition is the relevant one. 2719 dirent* dirBuf = reinterpret_cast<dirent*>(alloca(sizeof(struct dirent) + MAXNAMLEN + 1)); 2720 memset(dirBuf, 0, sizeof(struct dirent) + MAXNAMLEN + 1); 2721 2722 char* byteBuffer = reinterpret_cast<char*>(buffer); 2723 2724 // If they are reading from the root directory then, we simulate the . and .. entries since 2725 // these don't exist in the root directory. We also set the offset bias to make up for having 2726 // to simulate these entries. By this I mean that at file offset 64 we read the first entry in 2727 // the root directory that lives on disk. 2728 2729 // directory-relative index of the current direntry or winentry; 2730 // it is incremented for the simulated . and .. entries in the root directory too 2731 uint32* entryIndex = &reinterpret_cast<DirCookie*>(cookie)->fIndex; 2732 2733 int32 bias = 0; 2734 // disk offset = virtual offset - bias 2735 if (static_cast<ino_t>(fatNode->de_inode) == root_inode(fatVolume)) 2736 bias += 2 * sizeof(direntry); 2737 2738 if (*entryIndex * sizeof(direntry) >= fatNode->de_FileSize + bias) 2739 return B_OK; 2740 2741 if (static_cast<ino_t>(fatNode->de_inode) == root_inode(fatVolume)) { 2742 for (; *entryIndex < 2 && *_num < entriesRequested; ++*entryIndex, ++*_num) { 2743 dirBuf->d_ino = fatNode->de_inode; 2744 dirBuf->d_dev = volume->id; 2745 switch (*entryIndex) { 2746 case 0: 2747 dirBuf->d_name[0] = '.'; 2748 dirBuf->d_name[1] = '\0'; 2749 break; 2750 case 1: 2751 dirBuf->d_name[0] = '.'; 2752 dirBuf->d_name[1] = '.'; 2753 dirBuf->d_name[2] = '\0'; 2754 break; 2755 } 2756 dirBuf->d_reclen = GENERIC_DIRSIZ(dirBuf); 2757 2758 if (bufferSize < dirBuf->d_reclen) { 2759 if (*_num == 0) 2760 RETURN_ERROR(B_BUFFER_OVERFLOW) 2761 else 2762 return B_OK; 2763 } 2764 2765 memcpy(byteBuffer, dirBuf, dirBuf->d_reclen); 2766 2767 bufferSize -= dirBuf->d_reclen; 2768 byteBuffer += dirBuf->d_reclen; 2769 } 2770 } 2771 2772 buf* entriesBuf; 2773 // disk entries being read from 2774 mbnambuf longName; 2775 // filename after extraction and conversion from winentries 2776 mbnambuf_init(&longName); 2777 int chkSum = -1; 2778 // checksum of the filename 2779 int32 winChain = 0; 2780 // number of consecutive winentries we have found before reaching the corresponding 2781 // direntry 2782 bool done = false; 2783 status_t status = B_OK; 2784 while (bufferSize > 0 && *_num < entriesRequested && done == false) { 2785 int32 logicalCluster = de_cluster(fatVolume, (*entryIndex * sizeof(direntry)) - bias); 2786 // file-relative cluster number containing the next entry to read 2787 int32 clusterOffset = ((*entryIndex * sizeof(direntry)) - bias) & fatVolume->pm_crbomask; 2788 // byte offset into buf::b_data at which the inner loop starts reading 2789 int32 fileDiff = fatNode->de_FileSize - (*entryIndex * sizeof(direntry) - bias); 2790 // remaining data in the directory file 2791 if (fileDiff <= 0) 2792 break; 2793 int32 bytesLeft 2794 = min_c(static_cast<int32>(fatVolume->pm_bpcluster) - clusterOffset, fileDiff); 2795 // remaining data in the struct buf, excluding any area past EOF 2796 2797 int readSize; 2798 // how many bytes to read into the struct buf at a time; usually cluster size but 2799 // 512 bytes for the FAT12/16 root directory 2800 daddr_t readBlock; 2801 // volume-relative index of the readSize-sized block into entriesBuf 2802 size_t volumeCluster; 2803 // volume-relative cluster number containing the next entry to read 2804 status = B_FROM_POSIX_ERROR( 2805 pcbmap(fatNode, logicalCluster, &readBlock, &volumeCluster, &readSize)); 2806 if (status != B_OK) 2807 break; 2808 2809 status = B_FROM_POSIX_ERROR( 2810 bread(fatVolume->pm_devvp, readBlock, readSize, NOCRED, &entriesBuf)); 2811 if (status != B_OK) 2812 break; 2813 2814 bytesLeft = min_c(bytesLeft, readSize - entriesBuf->b_resid); 2815 if (bytesLeft == 0) { 2816 brelse(entriesBuf); 2817 status = B_IO_ERROR; 2818 break; 2819 } 2820 2821 // convert from DOS directory entries to FS-independent directory entries 2822 direntry* fatEntry; 2823 for (fatEntry = reinterpret_cast<direntry*>(entriesBuf->b_data + clusterOffset); 2824 reinterpret_cast<char*>(fatEntry) < entriesBuf->b_data + clusterOffset + bytesLeft 2825 && *_num < entriesRequested; fatEntry++, (*entryIndex)++) { 2826 // fatEntry is assumed to point to a struct direntry for now, but it may in fact 2827 // be a struct winentry; that case will handled below 2828 2829 // ff this is an unused entry, we can stop 2830 if (fatEntry->deName[0] == SLOT_EMPTY) { 2831 done = true; 2832 break; 2833 } 2834 2835 // skip deleted entries 2836 if (fatEntry->deName[0] == SLOT_DELETED) { 2837 chkSum = -1; 2838 mbnambuf_init(&longName); 2839 continue; 2840 } 2841 2842 // handle Win95 long directory entries 2843 if (fatEntry->deAttributes == ATTR_WIN95) { 2844 chkSum = win2unixfn(&longName, reinterpret_cast<winentry*>(fatEntry), chkSum, 2845 fatVolume); 2846 #ifdef DEBUG 2847 dprintf_winentry(fatVolume, reinterpret_cast<winentry*>(fatEntry), entryIndex); 2848 #endif 2849 winChain++; 2850 continue; 2851 } 2852 2853 // skip volume labels 2854 if (fatEntry->deAttributes & ATTR_VOLUME) { 2855 chkSum = -1; 2856 mbnambuf_init(&longName); 2857 continue; 2858 } 2859 2860 // Found a direntry. First, populate d_ino. 2861 ino_t ino; 2862 if (fatEntry->deAttributes & ATTR_DIRECTORY) { 2863 u_long entryCluster = getushort(fatEntry->deStartCluster); 2864 if (FAT32(fatVolume) != 0) 2865 entryCluster |= getushort(fatEntry->deHighClust) << 16; 2866 if (entryCluster == MSDOSFSROOT) 2867 ino = root_inode(fatVolume); 2868 else 2869 ino = DETOI(fatVolume, entryCluster, 0); 2870 } else { 2871 u_long dirOffset = *entryIndex * sizeof(direntry) - bias; 2872 if (IS_FIXED_ROOT(fatNode) == 0) { 2873 // we want a cluster-relative offset 2874 dirOffset = (dirOffset % fatVolume->pm_bpcluster); 2875 } 2876 ino = DETOI(fatVolume, volumeCluster, dirOffset); 2877 } 2878 status = assign_inode(bsdVolume, &ino); 2879 if (status != B_OK) 2880 break; 2881 dirBuf->d_ino = ino; 2882 2883 dirBuf->d_dev = volume->id; 2884 2885 // Is this direntry associated with a chain of previous winentries? 2886 if (chkSum != winChksum(fatEntry->deName)) { 2887 // no, just read the short file name from this direntry 2888 dos2unixfn(fatEntry->deName, reinterpret_cast<u_char*>(dirBuf->d_name), 2889 fatEntry->deLowerCase, fatVolume); 2890 dirBuf->d_reclen = GENERIC_DIRSIZ(dirBuf); 2891 mbnambuf_init(&longName); 2892 } else { 2893 // yes, use the long file name that was assembled from the previous winentry/ies 2894 mbnambuf_flush(&longName, dirBuf); 2895 } 2896 chkSum = -1; 2897 2898 if (bufferSize < dirBuf->d_reclen) { 2899 if (*_num == 0) { 2900 RETURN_ERROR(B_BUFFER_OVERFLOW); 2901 } else { 2902 done = true; 2903 // rewind to the start of the chain of winentries that precedes this direntry 2904 *entryIndex -= winChain; 2905 break; 2906 } 2907 } 2908 winChain = 0; 2909 2910 memcpy(byteBuffer, dirBuf, dirBuf->d_reclen); 2911 2912 // A single VFS dirent corresponds to 0 or more FAT winentries plus 1 FAT direntry. 2913 // Iteration code associated with direntries is placed here, instead of in the for 2914 // loop header, so it won't execute when the for loop continues early 2915 // after a winentry is found. 2916 bufferSize -= dirBuf->d_reclen; 2917 byteBuffer += dirBuf->d_reclen; 2918 ++*_num; 2919 } 2920 brelse(entriesBuf); 2921 } 2922 2923 #ifdef DEBUG 2924 PRINT("dosfs_readdir returning %" B_PRIu32 " dirents:\n", *_num); 2925 uint8* printCursor = reinterpret_cast<uint8*>(buffer); 2926 for (uint32 i = 0; i < *_num; i++) { 2927 dirent* bufferSlot = reinterpret_cast<dirent*>(printCursor); 2928 PRINT("buffer offset: %ld, d_dev: %" B_PRIdDEV ", d_ino: %" B_PRIdINO 2929 ", d_name: %s, d_reclen: %d\n", bufferSlot - buffer, bufferSlot->d_dev, 2930 bufferSlot->d_ino, bufferSlot->d_name, bufferSlot->d_reclen); 2931 printCursor += bufferSlot->d_reclen; 2932 } 2933 #endif 2934 2935 RETURN_ERROR(status); 2936 } 2937 2938 2939 static status_t 2940 dosfs_rewinddir(fs_volume* volume, fs_vnode* vnode, void* cookie) 2941 { 2942 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 2943 DirCookie* fatCookie = reinterpret_cast<DirCookie*>(cookie); 2944 2945 FUNCTION_START("%p\n", bsdNode); 2946 2947 WriteLocker locker(bsdNode->v_vnlock->haikuRW); 2948 2949 fatCookie->fIndex = 0; 2950 2951 return B_OK; 2952 } 2953 2954 2955 static status_t 2956 dosfs_open_attrdir(fs_volume* volume, fs_vnode* vnode, void** _cookie) 2957 { 2958 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 2959 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 2960 2961 FUNCTION_START("%p\n", bsdNode); 2962 2963 if (_dosfs_access(bsdVolume, bsdNode, O_RDONLY) != B_OK) 2964 RETURN_ERROR(B_NOT_ALLOWED); 2965 2966 if ((*_cookie = new(std::nothrow) int32) == NULL) 2967 RETURN_ERROR(B_NO_MEMORY); 2968 2969 *reinterpret_cast<int32*>(*_cookie) = 0; 2970 2971 return B_OK; 2972 } 2973 2974 2975 static status_t 2976 dosfs_close_attrdir(fs_volume* volume, fs_vnode* vnode, void* cookie) 2977 { 2978 FUNCTION_START("%p\n", vnode->private_node); 2979 2980 *reinterpret_cast<int32*>(cookie) = 1; 2981 2982 return B_OK; 2983 } 2984 2985 2986 static status_t 2987 dosfs_free_attrdir_cookie(fs_volume* volume, fs_vnode* vnode, void* cookie) 2988 { 2989 FUNCTION_START("%p\n", vnode->private_node); 2990 2991 if (cookie == NULL) 2992 return B_BAD_VALUE; 2993 2994 delete reinterpret_cast<int32*>(cookie); 2995 2996 return B_OK; 2997 } 2998 2999 3000 static status_t 3001 dosfs_read_attrdir(fs_volume* volume, fs_vnode* vnode, void* cookie, struct dirent* buffer, 3002 size_t bufferSize, uint32* _num) 3003 { 3004 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 3005 int32* fatCookie = reinterpret_cast<int32*>(cookie); 3006 3007 FUNCTION_START("%p\n", bsdNode); 3008 3009 *_num = 0; 3010 3011 ReadLocker locker(bsdNode->v_vnlock->haikuRW); 3012 3013 if ((*fatCookie == 0) && (bsdNode->v_mime != NULL)) { 3014 *_num = 1; 3015 strcpy(buffer->d_name, "BEOS:TYPE"); 3016 buffer->d_reclen = offsetof(struct dirent, d_name) + 10; 3017 } 3018 3019 *fatCookie = 1; 3020 3021 return B_OK; 3022 } 3023 3024 3025 static status_t 3026 dosfs_rewind_attrdir(fs_volume* volume, fs_vnode* vnode, void* cookie) 3027 { 3028 FUNCTION_START("%p\n", vnode->private_node); 3029 3030 if (cookie == NULL) 3031 return B_BAD_VALUE; 3032 3033 *reinterpret_cast<int32*>(cookie) = 0; 3034 3035 return B_OK; 3036 } 3037 3038 3039 static status_t 3040 dosfs_create_attr(fs_volume* volume, fs_vnode* vnode, const char* name, uint32 type, int openMode, 3041 void** _cookie) 3042 { 3043 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 3044 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 3045 3046 FUNCTION_START("%p\n", bsdNode); 3047 3048 ReadLocker locker(bsdNode->v_vnlock->haikuRW); 3049 3050 if (_dosfs_access(bsdVolume, bsdNode, open_mode_to_access(openMode)) != B_OK) 3051 RETURN_ERROR(B_NOT_ALLOWED); 3052 3053 if (strcmp(name, "BEOS:TYPE") != 0) 3054 return B_UNSUPPORTED; 3055 3056 if (bsdNode->v_mime == NULL) 3057 return B_BAD_VALUE; 3058 3059 AttrCookie* cookie = new(std::nothrow) AttrCookie; 3060 cookie->fMode = openMode; 3061 cookie->fType = FAT_ATTR_MIME; 3062 *_cookie = cookie; 3063 3064 return B_OK; 3065 } 3066 3067 3068 static status_t 3069 dosfs_open_attr(fs_volume* volume, fs_vnode* vnode, const char* name, int openMode, void** _cookie) 3070 { 3071 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume); 3072 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 3073 3074 FUNCTION_START("%p\n", bsdNode); 3075 3076 ReadLocker locker(bsdNode->v_vnlock->haikuRW); 3077 3078 if (_dosfs_access(bsdVolume, bsdNode, open_mode_to_access(openMode)) != B_OK) 3079 RETURN_ERROR(B_NOT_ALLOWED); 3080 3081 if (strcmp(name, "BEOS:TYPE") != 0) 3082 return B_UNSUPPORTED; 3083 3084 if (bsdNode->v_mime == NULL) 3085 return B_BAD_VALUE; 3086 3087 AttrCookie* cookie = new(std::nothrow) AttrCookie; 3088 cookie->fMode = openMode; 3089 cookie->fType = FAT_ATTR_MIME; 3090 *_cookie = cookie; 3091 3092 return B_OK; 3093 } 3094 3095 3096 static status_t 3097 dosfs_close_attr(fs_volume* volume, fs_vnode* vnode, void* cookie) 3098 { 3099 return B_OK; 3100 } 3101 3102 3103 static status_t 3104 dosfs_free_attr_cookie(fs_volume* volume, fs_vnode* vnode, void* cookie) 3105 { 3106 delete reinterpret_cast<AttrCookie*>(cookie); 3107 3108 return B_OK; 3109 } 3110 3111 3112 static status_t 3113 dosfs_read_attr(fs_volume* volume, fs_vnode* vnode, void* cookie, off_t pos, void* buffer, 3114 size_t* length) 3115 { 3116 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 3117 3118 FUNCTION_START("%p\n", bsdNode); 3119 3120 AttrCookie* fatCookie = reinterpret_cast<AttrCookie*>(cookie); 3121 if (fatCookie->fType != FAT_ATTR_MIME) 3122 return B_NOT_ALLOWED; 3123 if ((fatCookie->fMode & O_RWMASK) == O_WRONLY) 3124 return B_NOT_ALLOWED; 3125 3126 ReadLocker locker(bsdNode->v_vnlock->haikuRW); 3127 3128 if (bsdNode->v_mime == NULL) 3129 return B_BAD_VALUE; 3130 3131 if ((pos < 0) || (pos > static_cast<off_t>(strlen(bsdNode->v_mime)))) 3132 return B_BAD_VALUE; 3133 3134 ssize_t copied = strlcpy(reinterpret_cast<char*>(buffer), bsdNode->v_mime + pos, *length); 3135 if (copied < 0) 3136 return B_BAD_ADDRESS; 3137 3138 if (static_cast<size_t>(copied) < *length) 3139 *length = copied + 1; 3140 3141 return B_OK; 3142 } 3143 3144 3145 /*! suck up application attempts to set mime types; this hides an unsightly 3146 error message printed out by zip 3147 */ 3148 static status_t 3149 dosfs_write_attr(fs_volume* volume, fs_vnode* vnode, void* cookie, off_t pos, const void* buffer, 3150 size_t* length) 3151 { 3152 FUNCTION_START("%p\n", vnode->private_node); 3153 3154 AttrCookie* fatCookie = reinterpret_cast<AttrCookie*>(cookie); 3155 if (fatCookie->fType != FAT_ATTR_MIME) 3156 return B_NOT_ALLOWED; 3157 if ((fatCookie->fMode & O_RWMASK) == O_RDONLY) 3158 return B_NOT_ALLOWED; 3159 3160 return B_OK; 3161 } 3162 3163 3164 static status_t 3165 dosfs_read_attr_stat(fs_volume* volume, fs_vnode* vnode, void* cookie, struct stat* stat) 3166 { 3167 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node); 3168 3169 FUNCTION_START("%p\n", bsdNode); 3170 3171 AttrCookie* fatCookie = reinterpret_cast<AttrCookie*>(cookie); 3172 if (fatCookie->fType != FAT_ATTR_MIME) 3173 return B_NOT_ALLOWED; 3174 if ((fatCookie->fMode & O_RWMASK) == O_WRONLY) 3175 return B_NOT_ALLOWED; 3176 3177 ReadLocker locker(bsdNode->v_vnlock->haikuRW); 3178 3179 if (bsdNode->v_mime == NULL) 3180 return B_BAD_VALUE; 3181 3182 stat->st_type = B_MIME_STRING_TYPE; 3183 stat->st_size = strlen(bsdNode->v_mime) + 1; 3184 3185 return B_OK; 3186 } 3187 3188 3189 status_t 3190 dosfs_initialize(int fd, partition_id partitionID, const char* name, const char* parameterString, 3191 off_t partitionSize, disk_job_id job) 3192 { 3193 return _dosfs_initialize(fd, partitionID, name, parameterString, partitionSize, job); 3194 } 3195 3196 3197 status_t 3198 dosfs_uninitialize(int fd, partition_id partitionID, off_t partitionSize, uint32 blockSize, 3199 disk_job_id job) 3200 { 3201 return _dosfs_uninitialize(fd, partitionID, partitionSize, blockSize, job); 3202 } 3203 3204 3205 /*! Initialize a FreeBSD-style struct cdev. 3206 @param _readOnly As input, reflects the user-selected mount options; as output, will be set to 3207 true if the device is read-only. 3208 */ 3209 static status_t 3210 bsd_device_init(mount* bsdVolume, const dev_t devID, const char* deviceFile, cdev** bsdDevice, 3211 bool* _readOnly) 3212 { 3213 cdev* device = new(std::nothrow) cdev; 3214 if (device == NULL) 3215 RETURN_ERROR(B_NO_MEMORY); 3216 ObjectDeleter<cdev> deviceDeleter(device); 3217 3218 device->si_fd = -1; 3219 device->si_refcount = 0; 3220 device->si_mountpt = bsdVolume; 3221 device->si_name[0] = '\0'; 3222 strncpy(device->si_device, deviceFile, B_PATH_NAME_LENGTH - 1); 3223 device->si_mediasize = 0; 3224 device->si_id = devID; 3225 3226 device->si_geometry = new(std::nothrow) device_geometry; 3227 if (device->si_geometry == NULL) 3228 return B_NO_MEMORY; 3229 ObjectDeleter<device_geometry> geomDeleter(device->si_geometry); 3230 3231 // open read-only for now 3232 device->si_fd = open(deviceFile, O_RDONLY | O_NOCACHE); 3233 if (device->si_fd < 0) { 3234 if (errno == B_BUSY) 3235 INFORM("FAT driver does not permit multiple mount points at the same time\n"); 3236 RETURN_ERROR(B_FROM_POSIX_ERROR(errno)); 3237 } 3238 3239 // get device characteristics 3240 device_geometry* geometry = device->si_geometry; 3241 if (ioctl(device->si_fd, B_GET_GEOMETRY, geometry, sizeof(device_geometry)) == -1) { 3242 // support mounting disk images 3243 struct stat imageStat; 3244 if (fstat(device->si_fd, &imageStat) >= 0 && S_ISREG(imageStat.st_mode)) { 3245 geometry->bytes_per_sector = 0x200; 3246 geometry->sectors_per_track = 1; 3247 geometry->cylinder_count = imageStat.st_size / 0x200; 3248 geometry->head_count = 1; 3249 geometry->removable = true; 3250 geometry->read_only = !(imageStat.st_mode & S_IWUSR); 3251 geometry->write_once = false; 3252 #ifndef FS_SHELL 3253 geometry->bytes_per_physical_sector = 0x200; 3254 #endif 3255 device->si_mediasize = imageStat.st_size; 3256 } else { 3257 close(device->si_fd); 3258 RETURN_ERROR(B_FROM_POSIX_ERROR(errno)); 3259 } 3260 } else { 3261 device->si_mediasize = 1ULL * geometry->head_count * geometry->cylinder_count 3262 * geometry->sectors_per_track * geometry->bytes_per_sector; 3263 } 3264 3265 if (static_cast<uint64>(device->si_mediasize) > 2ULL << 34) { 3266 // the driver has not been tested on volumes > 32 GB 3267 INFORM("The FAT driver does not currently support volumes larger than 32 GB.\n"); 3268 close(device->si_fd); 3269 return B_UNSUPPORTED; 3270 } 3271 3272 if (geometry->bytes_per_sector != 0x200) { 3273 // FAT is compatible with 0x400, 0x800, and 0x1000 as well, but this driver has not 3274 // been tested with those values 3275 INFORM("driver does not yet support devices with > 1 block per sector\n"); 3276 close(device->si_fd); 3277 return B_UNSUPPORTED; 3278 } 3279 3280 if (geometry->read_only) { 3281 PRINT("%s is read-only\n", deviceFile); 3282 *_readOnly = true; 3283 } 3284 3285 if (*_readOnly == false) { 3286 // reopen it with read/write permissions 3287 close(device->si_fd); 3288 device->si_fd = open(deviceFile, O_RDWR | O_NOCACHE); 3289 if (device->si_fd < 0) 3290 RETURN_ERROR(B_FROM_POSIX_ERROR(errno)); 3291 } 3292 3293 // Prevent multiple simultaneous mounts. 3294 #ifndef FS_SHELL 3295 status_t status = _kern_lock_node(device->si_fd); 3296 if (status != B_OK) { 3297 close(device->si_fd); 3298 RETURN_ERROR(status); 3299 } 3300 #endif 3301 3302 deviceDeleter.Detach(); 3303 geomDeleter.Detach(); 3304 3305 *bsdDevice = device; 3306 3307 return B_OK; 3308 } 3309 3310 3311 status_t 3312 bsd_device_uninit(cdev* device) 3313 { 3314 if (device == NULL) 3315 return B_OK; 3316 3317 if (device->si_fd >= 0) { 3318 if (close(device->si_fd) != 0) 3319 RETURN_ERROR(B_FROM_POSIX_ERROR(errno)); 3320 } else { 3321 RETURN_ERROR(B_ERROR); 3322 } 3323 3324 delete device->si_geometry; 3325 3326 #ifndef FS_SHELL 3327 _kern_unlock_node(device->si_fd); 3328 #endif // FS_SHELL 3329 3330 delete device; 3331 3332 return B_OK; 3333 } 3334 3335 3336 /*! Create a FreeBSD-format vnode representing the device, to simulate a FreeBSD VFS environment 3337 for the ported driver code. 3338 */ 3339 static status_t 3340 dev_bsd_node_init(cdev* bsdDevice, vnode** devNode) 3341 { 3342 vnode* node; 3343 status_t status = B_FROM_POSIX_ERROR(getnewvnode(NULL, bsdDevice->si_mountpt, NULL, &node)); 3344 if (status != B_OK) 3345 RETURN_ERROR(status); 3346 3347 // Set up the device node members that are accessed by the driver. 3348 // We don't give this node any private data. 3349 node->v_type = VBLK; 3350 node->v_rdev = bsdDevice; 3351 node->v_mount = NULL; 3352 SLIST_INIT(&node->v_bufobj.bo_clusterbufs); 3353 SLIST_INIT(&node->v_bufobj.bo_fatbufs); 3354 SLIST_INIT(&node->v_bufobj.bo_emptybufs); 3355 node->v_bufobj.bo_clusters = 0; 3356 node->v_bufobj.bo_fatblocks = 0; 3357 node->v_bufobj.bo_empties = 0; 3358 rw_lock_init(&node->v_bufobj.bo_lock.haikuRW, "FAT v_bufobj"); 3359 3360 *devNode = node; 3361 3362 return B_OK; 3363 } 3364 3365 3366 status_t 3367 dev_bsd_node_uninit(vnode* devNode) 3368 { 3369 if (devNode == NULL) 3370 return B_OK; 3371 3372 rw_lock_write_lock(&devNode->v_bufobj.bo_lock.haikuRW); 3373 3374 // free cluster-size struct bufs: first b_data, and then the bufs themselves 3375 buf* listEntry; 3376 SLIST_FOREACH(listEntry, &devNode->v_bufobj.bo_clusterbufs, link) 3377 { 3378 free(listEntry->b_data); 3379 } 3380 while (!SLIST_EMPTY(&devNode->v_bufobj.bo_clusterbufs)) { 3381 listEntry = SLIST_FIRST(&devNode->v_bufobj.bo_clusterbufs); 3382 SLIST_REMOVE_HEAD(&devNode->v_bufobj.bo_clusterbufs, link); 3383 free(listEntry); 3384 } 3385 3386 // free the FAT-block size bufs 3387 listEntry = NULL; 3388 SLIST_FOREACH(listEntry, &devNode->v_bufobj.bo_fatbufs, link) 3389 { 3390 free(listEntry->b_data); 3391 } 3392 while (!SLIST_EMPTY(&devNode->v_bufobj.bo_fatbufs)) { 3393 listEntry = SLIST_FIRST(&devNode->v_bufobj.bo_fatbufs); 3394 SLIST_REMOVE_HEAD(&devNode->v_bufobj.bo_fatbufs, link); 3395 free(listEntry); 3396 } 3397 3398 // free the bufs that were just used as pointers to the block cache 3399 while (!SLIST_EMPTY(&devNode->v_bufobj.bo_emptybufs)) { 3400 listEntry = SLIST_FIRST(&devNode->v_bufobj.bo_emptybufs); 3401 SLIST_REMOVE_HEAD(&devNode->v_bufobj.bo_emptybufs, link); 3402 free(listEntry); 3403 } 3404 3405 rw_lock_write_unlock(&devNode->v_bufobj.bo_lock.haikuRW); 3406 rw_lock_destroy(&devNode->v_bufobj.bo_lock.haikuRW); 3407 3408 free(devNode); 3409 3410 return B_OK; 3411 } 3412 3413 3414 /*! Further setup will be done later for mnt_data, mnt_stat.f_mntonname, mnt_volentry, 3415 and mnt_cache. 3416 */ 3417 static status_t 3418 bsd_volume_init(fs_volume* fsVolume, const uint32 flags, mount** volume) 3419 { 3420 mount* bsdVolume = new(std::nothrow) mount; 3421 if (bsdVolume == NULL) 3422 return B_NO_MEMORY; 3423 ObjectDeleter<mount> volDeleter(bsdVolume); 3424 3425 bsdVolume->mnt_kern_flag = 0; 3426 bsdVolume->mnt_flag = 0; 3427 if ((flags & B_MOUNT_READ_ONLY) != 0) 3428 bsdVolume->mnt_flag |= MNT_RDONLY; 3429 3430 bsdVolume->mnt_vfc = new(std::nothrow) vfsconf; 3431 if ((bsdVolume)->mnt_vfc == NULL) 3432 return B_NO_MEMORY; 3433 bsdVolume->mnt_vfc->vfc_typenum = 1; 3434 // For the port, 1 is arbitrarily assigned as the type of this file system. 3435 // The member is accessed by the ported FreeBSD code but never used for anything. 3436 3437 bsdVolume->mnt_stat.f_iosize = FAT_IO_SIZE; 3438 3439 bsdVolume->mnt_data = NULL; 3440 3441 bsdVolume->mnt_iosize_max = FAT_IO_SIZE; 3442 3443 mutex_init(&bsdVolume->mnt_mtx.haikuMutex, "FAT volume"); 3444 3445 bsdVolume->mnt_fsvolume = fsVolume; 3446 3447 bsdVolume->mnt_volentry = -1; 3448 3449 if (init_vcache(bsdVolume) != B_OK) { 3450 mutex_destroy(&(bsdVolume)->mnt_mtx.haikuMutex); 3451 delete bsdVolume->mnt_vfc; 3452 return B_ERROR; 3453 } 3454 3455 bsdVolume->mnt_cache = NULL; 3456 3457 *volume = bsdVolume; 3458 3459 volDeleter.Detach(); 3460 3461 return B_OK; 3462 } 3463 3464 3465 status_t 3466 bsd_volume_uninit(struct mount* volume) 3467 { 3468 if (volume == NULL) 3469 return B_OK; 3470 3471 delete volume->mnt_vfc; 3472 3473 mutex_destroy(&volume->mnt_mtx.haikuMutex); 3474 3475 uninit_vcache(volume); 3476 3477 delete volume; 3478 3479 return B_OK; 3480 } 3481 3482 3483 /*! Set up a msdosfsmount as mount::mnt_data and initialize the block cache. 3484 3485 */ 3486 static status_t 3487 fat_volume_init(vnode* devvp, mount* bsdVolume, const uint64_t fatFlags, const char* oemPref) 3488 { 3489 // Read the boot sector of the filesystem, and then check the boot signature. 3490 // If not a dos boot sector then error out. 3491 cdev* dev = devvp->v_rdev; 3492 3493 uint8* bootsectorBuffer = static_cast<uint8*>(calloc(512, sizeof(char))); 3494 if (bootsectorBuffer == NULL) 3495 RETURN_ERROR(B_NO_MEMORY); 3496 MemoryDeleter bootsectorDeleter(bootsectorBuffer); 3497 if (read(dev->si_fd, bootsectorBuffer, 512) != 512) 3498 RETURN_ERROR(B_IO_ERROR); 3499 3500 enum FatType fatType; 3501 bool dos33; 3502 status_t status = check_bootsector(bootsectorBuffer, fatType, dos33); 3503 if (status != B_OK) 3504 RETURN_ERROR(status); 3505 3506 msdosfsmount* fatVolume = new(std::nothrow) msdosfsmount; 3507 if (fatVolume == NULL) 3508 RETURN_ERROR(B_NO_MEMORY); 3509 ObjectDeleter<msdosfsmount> volumeDeleter(fatVolume); 3510 3511 fatVolume->pm_cp = NULL; 3512 // Not implemented in port 3513 3514 fatVolume->pm_fsinfo = 0; 3515 fatVolume->pm_curfat = 0; 3516 fatVolume->pm_rootdirsize = 0; 3517 fatVolume->pm_fmod = 0; 3518 3519 fatVolume->pm_mountp = bsdVolume; 3520 fatVolume->pm_devvp = devvp; 3521 fatVolume->pm_odevvp = devvp; 3522 fatVolume->pm_bo = &devvp->v_bufobj; 3523 fatVolume->pm_dev = dev; 3524 3525 fatVolume->pm_flags = 0; 3526 3527 switch (fatType) { 3528 case fat12: 3529 fatVolume->pm_fatmask = FAT12_MASK; 3530 break; 3531 case fat16: 3532 fatVolume->pm_fatmask = FAT16_MASK; 3533 break; 3534 case fat32: 3535 fatVolume->pm_fatmask = FAT32_MASK; 3536 break; 3537 default: 3538 panic("invalid FAT type\n"); 3539 } 3540 3541 fatVolume->pm_uid = geteuid(); 3542 fatVolume->pm_gid = getegid(); 3543 3544 fatVolume->pm_dirmask = S_IXUSR | S_IXGRP | S_IXOTH | S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR; 3545 fatVolume->pm_mask = S_IXUSR | S_IXGRP | S_IXOTH | S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR; 3546 3547 // populate those msdosfsmount members that are pulled directly from the BPB 3548 status = parse_bpb(fatVolume, reinterpret_cast<bootsector*>(bootsectorBuffer), dos33); 3549 if (status != B_OK) 3550 RETURN_ERROR(status); 3551 3552 fatVolume->pm_BlkPerSec = fatVolume->pm_BytesPerSec / DEV_BSIZE; 3553 if (static_cast<off_t>(fatVolume->pm_HugeSectors * fatVolume->pm_BlkPerSec) * DEV_BSIZE 3554 > dev->si_mediasize) { 3555 INFORM("sector count exceeds media size (%" B_PRIdOFF " > %" B_PRIdOFF ")\n", 3556 static_cast<off_t>(fatVolume->pm_HugeSectors) * fatVolume->pm_BlkPerSec * DEV_BSIZE, 3557 dev->si_mediasize); 3558 return B_BAD_VALUE; 3559 } 3560 uint8 SecPerClust = fatVolume->pm_bpb.bpbSecPerClust; 3561 3562 // like FreeBSD, the port uses 512-byte blocks as the primary unit of disk data 3563 // rather then the device-dependent sector size 3564 fatVolume->pm_fsinfo *= fatVolume->pm_BlkPerSec; 3565 fatVolume->pm_HugeSectors *= fatVolume->pm_BlkPerSec; 3566 fatVolume->pm_HiddenSects *= fatVolume->pm_BlkPerSec; 3567 fatVolume->pm_FATsecs *= fatVolume->pm_BlkPerSec; 3568 SecPerClust *= fatVolume->pm_BlkPerSec; 3569 3570 fatVolume->pm_fatblk = fatVolume->pm_ResSectors * fatVolume->pm_BlkPerSec; 3571 3572 if (FAT32(fatVolume) == true) { 3573 fatVolume->pm_fatmult = 4; 3574 fatVolume->pm_fatdiv = 1; 3575 fatVolume->pm_firstcluster 3576 = fatVolume->pm_fatblk + fatVolume->pm_FATs * fatVolume->pm_FATsecs; 3577 } else { 3578 fatVolume->pm_curfat = 0; 3579 fatVolume->pm_rootdirblk 3580 = fatVolume->pm_fatblk + fatVolume->pm_FATs * fatVolume->pm_FATsecs; 3581 fatVolume->pm_rootdirsize = howmany(fatVolume->pm_RootDirEnts * sizeof(direntry), 3582 DEV_BSIZE); // in blocks 3583 fatVolume->pm_firstcluster = fatVolume->pm_rootdirblk + fatVolume->pm_rootdirsize; 3584 } 3585 3586 if (fatVolume->pm_HugeSectors <= fatVolume->pm_firstcluster) 3587 RETURN_ERROR(B_BAD_VALUE); 3588 3589 fatVolume->pm_maxcluster 3590 = (fatVolume->pm_HugeSectors - fatVolume->pm_firstcluster) / SecPerClust + 1; 3591 3592 if (FAT32(fatVolume) == false) { 3593 if (fatVolume->pm_maxcluster <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) { 3594 // This will usually be a floppy disk. This size makes sure that one FAT entry will 3595 // not be split across multiple blocks. 3596 fatVolume->pm_fatmult = 3; 3597 fatVolume->pm_fatdiv = 2; 3598 } else { 3599 fatVolume->pm_fatmult = 2; 3600 fatVolume->pm_fatdiv = 1; 3601 } 3602 } 3603 3604 fatVolume->pm_fatsize = fatVolume->pm_FATsecs * DEV_BSIZE; 3605 3606 uint32 fatCapacity = (fatVolume->pm_fatsize / fatVolume->pm_fatmult) * fatVolume->pm_fatdiv; 3607 if (fatVolume->pm_maxcluster >= fatCapacity) { 3608 INFORM("number of clusters (%ld) exceeds FAT capacity (%" B_PRIu32 ") " 3609 "(some clusters are inaccessible)\n", fatVolume->pm_maxcluster + 1, fatCapacity); 3610 fatVolume->pm_maxcluster = fatCapacity - 1; 3611 } 3612 3613 if (FAT12(fatVolume) != 0) 3614 fatVolume->pm_fatblocksize = 3 * 512; 3615 else 3616 fatVolume->pm_fatblocksize = DEV_BSIZE; 3617 fatVolume->pm_fatblocksec = fatVolume->pm_fatblocksize / DEV_BSIZE; 3618 fatVolume->pm_bnshift = ffs(DEV_BSIZE) - 1; 3619 3620 // compute mask and shift value for isolating cluster relative byte offsets and cluster 3621 // numbers from a file offset 3622 fatVolume->pm_bpcluster = SecPerClust * DEV_BSIZE; 3623 fatVolume->pm_crbomask = fatVolume->pm_bpcluster - 1; 3624 fatVolume->pm_cnshift = ffs(fatVolume->pm_bpcluster) - 1; 3625 3626 // this will be updated later if fsinfo exists 3627 fatVolume->pm_nxtfree = 3; 3628 3629 // check for valid cluster size - must be a power of 2 3630 if ((fatVolume->pm_bpcluster ^ (1 << fatVolume->pm_cnshift)) != 0) 3631 RETURN_ERROR(B_BAD_VALUE); 3632 3633 status = check_fat(fatVolume); 3634 if (status != B_OK) 3635 RETURN_ERROR(status); 3636 3637 // check that the partition is large enough to contain the file system 3638 bool readOnly = (fatFlags & MSDOSFSMNT_RONLY) != 0; 3639 device_geometry* geometry = dev->si_geometry; 3640 if (geometry != NULL 3641 && fatVolume->pm_HugeSectors 3642 > geometry->sectors_per_track * geometry->cylinder_count * geometry->head_count) { 3643 INFORM("dosfs: volume extends past end of partition, mounting read-only\n"); 3644 readOnly = true; 3645 } 3646 3647 status = read_label(fatVolume, dev->si_fd, bootsectorBuffer, dev->si_name); 3648 if (status != B_OK) 3649 RETURN_ERROR(status); 3650 3651 // Set up the block cache. 3652 // If the cached block size is ever changed, functions that work with the block cache 3653 // will need to be re-examined because they assume a size of 512 bytes 3654 // (e.g. dosfs_fsync, read_fsinfo, write_fsinfo, sync_clusters, discard_clusters, 3655 // dosfs_write_fs_stat, and the functions defined in vfs_bio.c). 3656 bsdVolume->mnt_cache 3657 = block_cache_create(dev->si_fd, fatVolume->pm_HugeSectors, CACHED_BLOCK_SIZE, readOnly); 3658 if (bsdVolume->mnt_cache == NULL) 3659 return B_ERROR; 3660 3661 status = read_fsinfo(fatVolume, devvp); 3662 if (status != B_OK) { 3663 block_cache_delete(bsdVolume->mnt_cache, false); 3664 return status; 3665 } 3666 3667 bsdVolume->mnt_data = fatVolume; 3668 3669 // allocate memory for the bitmap of allocated clusters, and then fill it in 3670 fatVolume->pm_inusemap = reinterpret_cast<u_int*>(malloc( 3671 howmany(fatVolume->pm_maxcluster + 1, N_INUSEBITS) * sizeof(*fatVolume->pm_inusemap))); 3672 if (fatVolume->pm_inusemap == NULL) { 3673 block_cache_delete(bsdVolume->mnt_cache, false); 3674 bsdVolume->mnt_data = NULL; 3675 RETURN_ERROR(B_NO_MEMORY); 3676 } 3677 MemoryDeleter inusemapDeleter(fatVolume->pm_inusemap); 3678 3679 rw_lock_init(&fatVolume->pm_fatlock.haikuRW, "fatlock"); 3680 3681 // have the inuse map filled in 3682 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW); 3683 status = B_FROM_POSIX_ERROR(fillinusemap(fatVolume)); 3684 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW); 3685 if (status != 0) { 3686 rw_lock_destroy(&fatVolume->pm_fatlock.haikuRW); 3687 block_cache_delete(bsdVolume->mnt_cache, false); 3688 bsdVolume->mnt_data = NULL; 3689 RETURN_ERROR(status); 3690 } 3691 3692 // some flags from the FreeBSD driver are not supported in the port 3693 ASSERT((fatVolume->pm_flags 3694 & (MSDOSFSMNT_SHORTNAME | MSDOSFSMNT_LONGNAME | MSDOSFSMNT_NOWIN95 | MSDOSFS_ERR_RO)) 3695 == 0); 3696 fatVolume->pm_flags |= fatFlags; 3697 3698 if (readOnly == true) { 3699 fatVolume->pm_flags |= MSDOSFSMNT_RONLY; 3700 bsdVolume->mnt_flag |= MNT_RDONLY; 3701 } else { 3702 status = B_FROM_POSIX_ERROR(markvoldirty(fatVolume, 1)); 3703 if (status != B_OK) { 3704 rw_lock_destroy(&fatVolume->pm_fatlock.haikuRW); 3705 block_cache_delete(bsdVolume->mnt_cache, false); 3706 bsdVolume->mnt_data = NULL; 3707 RETURN_ERROR(status); 3708 } 3709 fatVolume->pm_fmod = 1; 3710 } 3711 3712 status = iconv_init(fatVolume, oemPref); 3713 if (status != B_OK) { 3714 rw_lock_destroy(&fatVolume->pm_fatlock.haikuRW); 3715 block_cache_delete(bsdVolume->mnt_cache, false); 3716 bsdVolume->mnt_data = NULL; 3717 RETURN_ERROR(status); 3718 } 3719 3720 rw_lock_init(&fatVolume->pm_checkpath_lock.haikuRW, "fat cp"); 3721 3722 volumeDeleter.Detach(); 3723 inusemapDeleter.Detach(); 3724 3725 return B_OK; 3726 } 3727 3728 3729 /*! Clean up the msdosfsmount and the block cache. 3730 @pre pm_devvp and pm_dev still exist. 3731 */ 3732 status_t 3733 fat_volume_uninit(msdosfsmount* volume) 3734 { 3735 if (volume == NULL) 3736 return B_OK; 3737 3738 status_t status = B_OK; 3739 if (((volume)->pm_flags & MSDOSFSMNT_RONLY) == 0) { 3740 rw_lock_write_lock(&volume->pm_fatlock.haikuRW); 3741 status = B_FROM_POSIX_ERROR(markvoldirty((volume), 0)); 3742 rw_lock_write_unlock(&volume->pm_fatlock.haikuRW); 3743 if (status != B_OK) { 3744 markvoldirty((volume), 1); 3745 REPORT_ERROR(status); 3746 } 3747 } 3748 3749 if ((volume->pm_flags & MSDOSFSMNT_KICONV) != 0 && msdosfs_iconv != NULL) { 3750 if (volume->pm_w2u != NULL) 3751 msdosfs_iconv->close(volume->pm_w2u); 3752 if (volume->pm_u2w != NULL) 3753 msdosfs_iconv->close(volume->pm_u2w); 3754 if (volume->pm_d2u != NULL) 3755 msdosfs_iconv->close(volume->pm_d2u); 3756 if (volume->pm_u2d != NULL) 3757 msdosfs_iconv->close(volume->pm_u2d); 3758 delete msdosfs_iconv; 3759 msdosfs_iconv = NULL; 3760 } 3761 3762 status = write_fsinfo(volume); 3763 if (status != B_OK) 3764 REPORT_ERROR(status); 3765 3766 if (volume->pm_mountp->mnt_cache != NULL) { 3767 block_cache_delete(volume->pm_mountp->mnt_cache, 3768 (volume->pm_flags & MSDOSFSMNT_RONLY) == 0); 3769 volume->pm_mountp->mnt_cache = NULL; 3770 } 3771 3772 free(volume->pm_inusemap); 3773 3774 rw_lock_destroy(&volume->pm_fatlock.haikuRW); 3775 rw_lock_destroy(&volume->pm_checkpath_lock.haikuRW); 3776 3777 delete volume; 3778 3779 return status; 3780 } 3781 3782 3783 static status_t 3784 iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset, size_t size, 3785 struct file_io_vec* vecs, size_t* _count) 3786 { 3787 vnode* bsdNode = reinterpret_cast<vnode*>(cookie); 3788 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdNode->v_mount->mnt_data); 3789 3790 return file_map_translate(bsdNode->v_file_map, offset, size, vecs, _count, 3791 fatVolume->pm_bpcluster); 3792 } 3793 3794 3795 static status_t 3796 iterative_io_finished_hook(void* cookie, io_request* request, status_t status, bool partialTransfer, 3797 size_t bytesTransferred) 3798 { 3799 vnode* bsdNode = reinterpret_cast<vnode*>(cookie); 3800 3801 rw_lock_read_unlock(&bsdNode->v_vnlock->haikuRW); 3802 3803 return B_OK; 3804 } 3805 3806 3807 static uint32 3808 dosfs_get_supported_operations(partition_data* partition, uint32 mask) 3809 { 3810 FUNCTION(); 3811 3812 return B_DISK_SYSTEM_SUPPORTS_INITIALIZING | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME 3813 | B_DISK_SYSTEM_SUPPORTS_WRITING; 3814 } 3815 3816 3817 static status_t 3818 dos_std_ops(int32 op, ...) 3819 { 3820 switch (op) { 3821 case B_MODULE_INIT: 3822 FUNCTION_START("B_MODULE_INIT\n"); 3823 #ifdef _KERNEL_MODE 3824 add_debugger_command("fat", kprintf_volume, "dump a FAT private volume"); 3825 add_debugger_command("fat_node", kprintf_node, "dump a FAT private node"); 3826 #endif // _KERNEL_MODE 3827 break; 3828 3829 case B_MODULE_UNINIT: 3830 FUNCTION_START("B_MODULE_UNINIT\n"); 3831 #ifdef _KERNEL_MODE 3832 remove_debugger_command("fat", kprintf_volume); 3833 remove_debugger_command("fat_node", kprintf_node); 3834 #endif // _KERNEL_MODE 3835 break; 3836 3837 default: 3838 return B_ERROR; 3839 } 3840 3841 return B_OK; 3842 } 3843 3844 3845 fs_volume_ops gFATVolumeOps = { 3846 &dosfs_unmount, 3847 &dosfs_read_fs_stat, 3848 &dosfs_write_fs_stat, 3849 &dosfs_sync, 3850 &dosfs_read_vnode, 3851 3852 // index directory & index operations 3853 NULL, //&fs_open_index_dir, 3854 NULL, //&fs_close_index_dir, 3855 NULL, //&fs_free_index_dir_cookie, 3856 NULL, //&fs_read_index_dir, 3857 NULL, //&fs_rewind_index_dir, 3858 3859 NULL, //&fs_create_index, 3860 NULL, //&fs_remove_index, 3861 NULL, //&fs_stat_index, 3862 3863 // query operations 3864 NULL, //&fs_open_query, 3865 NULL, //&fs_close_query, 3866 NULL, //&fs_free_query_cookie, 3867 NULL, //&fs_read_query, 3868 NULL, //&fs_rewind_query, 3869 }; 3870 3871 3872 fs_vnode_ops gFATVnodeOps = { 3873 // vnode operations 3874 &dosfs_walk, 3875 NULL, // fs_get_vnode_name, 3876 &dosfs_release_vnode, 3877 &dosfs_remove_vnode, 3878 3879 // VM file access 3880 &dosfs_can_page, 3881 &dosfs_read_pages, 3882 &dosfs_write_pages, 3883 3884 &dosfs_io, 3885 NULL, // cancel_io() 3886 3887 &dosfs_get_file_map, 3888 3889 NULL, // fs_ioctl() 3890 NULL, // fs_set_flags, 3891 NULL, // fs_select 3892 NULL, // fs_deselect 3893 &dosfs_fsync, 3894 3895 NULL, // fs_read_symlink, 3896 NULL, // fs_create_symlink, 3897 3898 &dosfs_link, 3899 &dosfs_unlink, 3900 &dosfs_rename, 3901 3902 &dosfs_access, 3903 &dosfs_rstat, 3904 &dosfs_wstat, 3905 NULL, // fs_preallocate, 3906 3907 // file operations 3908 &dosfs_create, 3909 &dosfs_open, 3910 &dosfs_close, 3911 &dosfs_free_cookie, 3912 &dosfs_read, 3913 &dosfs_write, 3914 3915 // directory operations 3916 &dosfs_mkdir, 3917 &dosfs_rmdir, 3918 &dosfs_opendir, 3919 &dosfs_closedir, 3920 &dosfs_free_dircookie, 3921 &dosfs_readdir, 3922 &dosfs_rewinddir, 3923 3924 // attribute directory operations 3925 &dosfs_open_attrdir, 3926 &dosfs_close_attrdir, 3927 &dosfs_free_attrdir_cookie, 3928 &dosfs_read_attrdir, 3929 &dosfs_rewind_attrdir, 3930 3931 // attribute operations 3932 &dosfs_create_attr, 3933 &dosfs_open_attr, 3934 &dosfs_close_attr, 3935 &dosfs_free_attr_cookie, 3936 &dosfs_read_attr, 3937 &dosfs_write_attr, 3938 3939 &dosfs_read_attr_stat, 3940 NULL, // fs_write_attr_stat, 3941 NULL, // fs_rename_attr, 3942 NULL, // fs_remove_attr 3943 }; 3944 3945 3946 static file_system_module_info sFATBSDFileSystem = { 3947 { 3948 "file_systems/fat" B_CURRENT_FS_API_VERSION, 3949 0, 3950 dos_std_ops, 3951 }, 3952 "fat", // short_name 3953 "FAT32 File System", // pretty_name 3954 3955 // DDM flags 3956 0 3957 // | B_DISK_SYSTEM_SUPPORTS_CHECKING 3958 // | B_DISK_SYSTEM_SUPPORTS_REPAIRING 3959 // | B_DISK_SYSTEM_SUPPORTS_RESIZING 3960 // | B_DISK_SYSTEM_SUPPORTS_MOVING 3961 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME 3962 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS 3963 | B_DISK_SYSTEM_SUPPORTS_INITIALIZING 3964 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME 3965 // | B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING 3966 // | B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING_WHILE_MOUNTED 3967 // | B_DISK_SYSTEM_SUPPORTS_CHECKING_WHILE_MOUNTED 3968 // | B_DISK_SYSTEM_SUPPORTS_REPAIRING_WHILE_MOUNTED 3969 // | B_DISK_SYSTEM_SUPPORTS_RESIZING_WHILE_MOUNTED 3970 // | B_DISK_SYSTEM_SUPPORTS_MOVING_WHILE_MOUNTED 3971 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME_WHILE_MOUNTED 3972 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS_WHILE_MOUNTED 3973 | B_DISK_SYSTEM_SUPPORTS_WRITING, 3974 3975 // scanning 3976 &dosfs_identify_partition, 3977 &dosfs_scan_partition, 3978 &dosfs_free_identify_partition_cookie, 3979 NULL, // free_partition_content_cookie 3980 3981 &dosfs_mount, 3982 3983 // capability querying operations 3984 &dosfs_get_supported_operations, 3985 3986 NULL, // validate_resize 3987 NULL, // validate_move 3988 NULL, // validate_set_content_name 3989 NULL, // validate_set_content_parameters 3990 NULL, // validate_initialize 3991 3992 // shadow partition modification 3993 NULL, // shadow_changed 3994 3995 // writing 3996 NULL, // defragment 3997 NULL, // repair 3998 NULL, // resize 3999 NULL, // move 4000 NULL, // set_content_name 4001 NULL, // set_content_parameters 4002 dosfs_initialize, 4003 dosfs_uninitialize}; 4004 4005 4006 module_info* modules[] = { 4007 reinterpret_cast<module_info*>(&sFATBSDFileSystem), 4008 NULL, 4009 }; 4010