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