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