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