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