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