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