xref: /haiku/src/add-ons/kernel/file_systems/fat/support.cpp (revision 344ded80d400028c8f561b4b876257b94c12db4a)
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 #include "support.h"
59 
60 #ifdef FS_SHELL
61 #include "fssh_api_wrapper.h"
62 #else // !FS_SHELL
63 #include <dirent.h>
64 #include <malloc.h>
65 
66 #include <NodeMonitor.h>
67 #include <SupportDefs.h>
68 
69 #include <AutoDeleter.h>
70 #include <file_systems/mime_ext_table.h>
71 #include <real_time_clock.h>
72 #include <util/AutoLock.h>
73 #endif // !FS_SHELL
74 
75 #include "debug.h"
76 #include "dosfs.h"
77 #include "vcache.h"
78 
79 
80 extern struct iconv_functions* msdosfs_iconv;
81 
82 struct emptyDir gDirTemplate = {
83 	//	{	".          ",				/* the . entry */
84 	{
85 		{'.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
86 		ATTR_DIRECTORY, /* file attribute */
87 		0, /* reserved */
88 		0, {0, 0}, {0, 0}, /* create time & date */
89 		{0, 0}, /* access date */
90 		{0, 0}, /* high bits of start cluster */
91 		{210, 4}, {210, 4}, /* modify time & date */
92 		{0, 0}, /* startcluster */
93 		{0, 0, 0, 0} /* filesize */
94 	},
95 	//	{	"..         ",				/* the .. entry */
96 	{
97 		{'.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
98 		ATTR_DIRECTORY, /* file attribute */
99 		0, /* reserved */
100 		0, {0, 0}, {0, 0}, /* create time & date */
101 		{0, 0}, /* access date */
102 		{0, 0}, /* high bits of start cluster */
103 		{210, 4}, {210, 4}, /* modify time & date */
104 		{0, 0}, /* startcluster */
105 		{0, 0, 0, 0} /* filesize */
106 	}};
107 
108 
109 ComponentName::ComponentName(u_int64_t flags, ucred* cred, enum nameiop nameiop, int lkflags,
110 	const char* nameptr)
111 {
112 	fData.cn_flags = flags;
113 	fData.cn_cred = cred;
114 	fData.cn_nameiop = nameiop;
115 	fData.cn_lkflags = lkflags;
116 	fData.cn_nameptr = strdup(nameptr);
117 	fData.cn_namelen = strlen(fData.cn_nameptr);
118 }
119 
120 
121 ComponentName::~ComponentName()
122 {
123 	free(fData.cn_nameptr);
124 }
125 
126 
127 struct componentname*
128 ComponentName::Data()
129 {
130 	return &fData;
131 }
132 
133 
134 bool
135 is_filename_legal(const char* name)
136 {
137 	if (name == NULL)
138 		return false;
139 
140 	// illegal characters in a long file name
141 	const char illegal[] = "\\/:*?\"<>|";
142 
143 	uint32 len = strlen(name);
144 	if (len <= 0)
145 		return false;
146 
147 	// names with only an extension are not allowed
148 	if (strrchr(name, '.') == name)
149 		return false;
150 
151 	// win2unixfn will trim trailing dots and spaces from the FAT longname
152 	// but it does expect there to be something there other than dots and spaces.
153 	bool hasContent = false;
154 	for (uint32 i = 0; i < len && hasContent == false; i++) {
155 		if (name[i] != ' ' && name[i] != '.')
156 			hasContent = true;
157 	}
158 	if (hasContent == false)
159 		return false;
160 
161 	for (uint32 i = 0; i < len; i++) {
162 		if (name[i] & 0x80)
163 			continue; // belongs to an utf8 char
164 		if (strchr(illegal, name[i]))
165 			return false;
166 		if (static_cast<unsigned char>(name[i]) < 32)
167 			return false;
168 	}
169 	return true;
170 }
171 
172 
173 bool
174 is_shortname_legal(const u_char* name)
175 {
176 	// if the volume is ever mounted on Windows, short name should not be the same as that
177 	// of a Windows device
178 	const char* device_names[] = {"CON        ", "PRN        ", "AUX        ", "NUL        ",
179 		"COM0       ", "COM1       ", "COM2       ", "COM3       ", "COM4       ", "COM5       ",
180 		"COM6       ", "COM7       ", "COM8       ", "COM9       ", "LPT0       ", "LPT1       ",
181 		"LPT2       ", "LPT3       ", "LPT4       ", "LPT5       ", "LPT6       ", "LPT7       ",
182 		"LPT8       ", "LPT9       ", NULL};
183 
184 	for (uint32 i = 0; device_names[i]; i++) {
185 		// only first 8 characters seem to matter
186 		if (memcmp(name, device_names[i], 8) == 0)
187 			return false;
188 	}
189 
190 	return true;
191 }
192 
193 
194 /*! Convert a volume label from a null-terminated string to the format stored on disk.
195 	@param label A C string of size LABEL_CSTRING.
196 	@post The first LABEL_LENGTH characters of label contain an all-caps space-padded label, or
197 	an error is returned.
198 */
199 status_t
200 label_to_fat(char* label)
201 {
202 	if (label[0] == 0) {
203 		// bad name, kiddo
204 		INFORM("label_to_fat: empty name\n");
205 		return B_BAD_VALUE;
206 	}
207 
208 	if (label[0] == ' ') {
209 		INFORM("label_to_fat: vol name starts with space\n");
210 		return B_BAD_VALUE;
211 	}
212 
213 	uint32 length = strlen(label);
214 	if (length > LABEL_LENGTH)
215 		return B_NAME_TOO_LONG;
216 
217 	for (uint32 i = 0; i < length; i++) {
218 		char c = label[i];
219 		if (c >= 'a' && c <= 'z')
220 			c+= 'A' - 'a';
221 		// spaces acceptable in volume names
222 		if (strchr(sAcceptable, c) || c == ' ') {
223 			label[i] = c;
224 		} else {
225 			INFORM("label_to_fat: vol name contains illegal char (%c at index %" B_PRIu32
226 				")\n", label[i], i);
227 			return B_BAD_VALUE;
228 		}
229 	}
230 
231 	for (uint32 i = length; i < LABEL_LENGTH; i++)
232 		label[i] = ' ';
233 
234 	PRINT("label_to_fat: converted to [%11.11s].\n", label);
235 
236 	return B_OK;
237 }
238 
239 
240 /*! Convert a volume label from the format stored on disk into a normal string.
241 	@param name A character array of length 11 or a C string of size 12.
242 	@post Name is null-terminated after the last non-space character, and converted to lower case.
243 */
244 void
245 label_from_fat(char* name)
246 {
247 	int i;
248 	for (i = 10; i > 0; i--) {
249 		if (name[i] != ' ')
250 			break;
251 	}
252 	name[i + 1] = 0;
253 
254 	for (; i >= 0; i--) {
255 		if (name[i] >= 'A' && name[i] <= 'Z')
256 			name[i] += 'a' - 'A';
257 	}
258 }
259 
260 
261 /*! Return the volume label.
262 	@param fd The device file, specified independently of volume because in
263 	dosfs_identify_partition, volume->pm_dev will not be initialized.
264 	@param label The pre-allocated (LABEL_CSTRING bytes) string into which the label is copied.
265 	@pre The following members of volume are initialized:  pm_rootdirsize, pm_BlkPerSec,
266 	pm_BytesPerSec, pm_bpcluster, pm_rootdirblk, pm_bnshift.  For a FAT32 volume,
267 	pm_firstcluster must also be initialized.
268 	@post If volume is an old DOS 3.3 era FAT volume, which will not have a label, then label is
269 	set to an empty string and the function returns B_OK. For other volumes, the root
270 	directory label is returned in label, if found; if not, the BPB label is returned.
271 */
272 status_t
273 read_label(const msdosfsmount* volume, int fd, const uint8* buffer, char* label)
274 {
275 	*label = '\0';
276 
277 	bool fat32 = FAT32(volume);
278 	uint8 check = fat32 == true ? 0x42 : 0x26;
279 	uint8 offset = fat32 == true ? 0x47 : 0x2b;
280 	if (buffer[check] == EXBOOTSIG && memcmp(buffer + offset, "           ", LABEL_LENGTH) != 0) {
281 		memcpy(label, buffer + offset, LABEL_LENGTH);
282 		label_from_fat(label);
283 	} else {
284 		return B_OK;
285 	}
286 
287 	// read the root directory
288 	if (volume->pm_mountp != NULL)
289 		volume->pm_mountp->mnt_volentry = -1;
290 	int32 rootDirBytes;
291 	if (fat32 == true)
292 		rootDirBytes = volume->pm_bpcluster;
293 			// we will only search the first cluster of the root directory, for FAT32 volumes
294 	else
295 		rootDirBytes = (volume->pm_rootdirsize / volume->pm_BlkPerSec) * volume->pm_BytesPerSec;
296 
297 	uint8* rootDirBuffer = static_cast<uint8*>(calloc(rootDirBytes, sizeof(char)));
298 	daddr_t rootDirBlock = volume->pm_rootdirblk;
299 	if (fat32 == true)
300 		rootDirBlock = cntobn(volume, rootDirBlock);
301 	off_t rootDirPos = de_bn2off(volume, rootDirBlock);
302 
303 	int32 bytesRead = read_pos(fd, rootDirPos, rootDirBuffer, rootDirBytes);
304 	if (bytesRead != rootDirBytes) {
305 		free(rootDirBuffer);
306 		RETURN_ERROR(B_IO_ERROR);
307 	}
308 
309 	// find volume label entry in the root directory (supercedes any label in the bpb)
310 	// continue silently if not found.
311 	uint32 direntrySize = sizeof(struct direntry);
312 	uint32 rootDirEntries = rootDirBytes / direntrySize;
313 	struct direntry* direntry = NULL;
314 	for (uint32 i = 0;
315 		i < rootDirEntries && (direntry == NULL || direntry->deName[0] != SLOT_EMPTY); ++i) {
316 		direntry = reinterpret_cast<struct direntry*>(rootDirBuffer + direntrySize * i);
317 		if (direntry->deName[0] != SLOT_DELETED && direntry->deAttributes != ATTR_WIN95
318 			&& (direntry->deAttributes & ATTR_VOLUME) != 0) {
319 			memcpy(label, direntry->deName, 11);
320 			label_from_fat(label);
321 			PRINT("found volume label in root directory:  %s at i = %" B_PRIu32 "\n", label, i);
322 			if (volume->pm_mountp != NULL)
323 				volume->pm_mountp->mnt_volentry = i;
324 			break;
325 		}
326 	}
327 
328 	free(rootDirBuffer);
329 
330 	return B_OK;
331 }
332 
333 
334 /*! Determine whether the bootsector is from a FAT volume and, if so, the type of FAT and, if
335 	FAT12 or FAT16, if it is formatted in the old DOS 3.3 format.
336 */
337 status_t
338 check_bootsector(const uint8* bootsector, FatType& _type, bool& _dos33)
339 {
340 	// check the two FAT boot signature bytes
341 	if (bootsector[0x1fe] != BOOTSIG0 || bootsector[0x1ff] != BOOTSIG1)
342 		return B_BAD_VALUE;
343 
344 	// check for indications of a non-FAT filesystem
345 	if (!memcmp((uint8*) bootsector + 3, "NTFS    ", 8)
346 		|| !memcmp((uint8*) bootsector + 3, "HPFS    ", 8)) {
347 		return B_BAD_VALUE;
348 	}
349 
350 	// check the validity of a FAT BPB value to verify it is really FAT
351 	uint32 bytesPerSector = read16(bootsector, 0Xb);
352 	if (bytesPerSector != 512 && bytesPerSector != 1024 && bytesPerSector != 2048
353 		&& bytesPerSector != 4096) {
354 		return B_BAD_VALUE;
355 	}
356 
357 	// It appears to be a valid FAT volume of some kind.
358 	// Determine the FAT type by counting the data clusters.
359 	uint32 sectorsPerCluster = bootsector[0xd];
360 	if (sectorsPerCluster == 0)
361 		return B_BAD_VALUE;
362 	uint32 rootDirEntries = read16(bootsector, 0x11);
363 	uint32 rootDirSectors = ((rootDirEntries * 32) + (bytesPerSector - 1)) / bytesPerSector;
364 	uint32 sectorsPerFat = read16(bootsector, 0x16);
365 	if (sectorsPerFat == 0)
366 		sectorsPerFat = read32(bootsector, 0x24);
367 	uint32 totalSectors = read16(bootsector, 0x13);
368 	if (totalSectors == 0)
369 		totalSectors = read32(bootsector, 0x20);
370 	uint32 reservedSectors = read16(bootsector, 0xe);
371 	uint32 fatCount = bootsector[0x10];
372 	uint32 dataSectors
373 		= totalSectors - (reservedSectors + (fatCount * sectorsPerFat) + rootDirSectors);
374 	uint32 clusterCount = dataSectors / sectorsPerCluster;
375 	if (clusterCount < 4085)
376 		_type = fat12;
377 	else if (clusterCount < 65525)
378 		_type = fat16;
379 	else
380 		_type = fat32;
381 
382 	// determine whether we are dealing with an old DOS 3.3 format
383 	// volume by checking for the FAT extended boot signature
384 	if (_type != fat32 && bootsector[0x26] != EXBOOTSIG)
385 		_dos33 = true;
386 	else
387 		_dos33 = false;
388 
389 	return B_OK;
390 }
391 
392 
393 /*! Populate volume with values from the BIOS parameter block.
394 	@pre Volume is already allocated and pm_fatmask has been set; if it is a temp object created
395 	for use in dosfs_identify_partition, that will be indicated by a NULL volume->pm_mount.
396 	@post Those members of volume that can be read directly from the BPB are initialized.
397 	pm_HugeSectors will be initialized, regardless of which	field the BPB stores the sector count
398 	in (in contrast, pm_Sectors will not always contain an accurate count).
399 */
400 status_t
401 parse_bpb(msdosfsmount* volume, const bootsector* bootsector, bool dos33)
402 {
403 	status_t status = B_OK;
404 	const universal_byte_bpb* bpb
405 		= reinterpret_cast<const universal_byte_bpb*>(bootsector->bs33.bsBPB);
406 
407 	// first fill in the universal fields from the bpb
408 	volume->pm_BytesPerSec = getushort(bpb->bpbBytesPerSec);
409 	volume->pm_bpb.bpbSecPerClust = bpb->bpbSecPerClust;
410 	volume->pm_ResSectors = getushort(bpb->bpbResSectors);
411 	volume->pm_FATs = bpb->bpbFATs;
412 	volume->pm_RootDirEnts = getushort(bpb->bpbRootDirEnts);
413 	volume->pm_Sectors = getushort(bpb->bpbSectors);
414 	volume->pm_Media = bpb->bpbMedia;
415 	volume->pm_FATsecs = volume->pm_bpb.bpbFATsecs = getushort(bpb->bpbFATsecs);
416 	volume->pm_SecPerTrack = getushort(bpb->bpbSecPerTrack);
417 	volume->pm_Heads = getushort(bpb->bpbHeads);
418 
419 	// check the validity of some universal fields
420 	if (volume->pm_BytesPerSec != 0x200 && volume->pm_BytesPerSec != 0x400
421 		&& volume->pm_BytesPerSec != 0x800 && volume->pm_BytesPerSec != 0x1000) {
422 		INFORM("invalid bytes per sector (%d)\n", volume->pm_BytesPerSec);
423 		status = B_BAD_VALUE;
424 	} else if (volume->pm_BytesPerSec < DEV_BSIZE) {
425 		INFORM("invalid bytes per sector (%d)\n", volume->pm_BytesPerSec);
426 		status = B_BAD_VALUE;
427 	} else if (volume->pm_bpb.bpbSecPerClust != 1 && volume->pm_bpb.bpbSecPerClust != 2
428 		&& volume->pm_bpb.bpbSecPerClust != 4 && volume->pm_bpb.bpbSecPerClust != 8
429 		&& volume->pm_bpb.bpbSecPerClust != 16 && volume->pm_bpb.bpbSecPerClust != 32
430 		&& volume->pm_bpb.bpbSecPerClust != 64 && volume->pm_bpb.bpbSecPerClust != 128) {
431 		INFORM("invalid sectors per cluster (%" B_PRIu8 ")\n", volume->pm_bpb.bpbSecPerClust);
432 		status = B_BAD_VALUE;
433 	} else if (volume->pm_BytesPerSec * volume->pm_bpb.bpbSecPerClust > 0x10000) {
434 		INFORM("invalid bytes per cluster (%" B_PRIu16 ")\n",
435 			volume->pm_BytesPerSec * volume->pm_bpb.bpbSecPerClust);
436 		status = B_BAD_VALUE;
437 	} else if (volume->pm_FATs == 0 || volume->pm_FATs > 8) {
438 		INFORM("unreasonable fat count (%" B_PRIu8 ")\n", volume->pm_FATs);
439 		status = B_BAD_VALUE;
440 	} else if (volume->pm_Media != 0xf0 && volume->pm_Media < 0xf8) {
441 		INFORM("invalid media descriptor byte\n");
442 		status = B_BAD_VALUE;
443 	}
444 	if (status != B_OK)
445 		return status;
446 
447 	// now become more discerning citizens
448 	if (dos33 == true && (FAT12(volume) != 0 || FAT16(volume) != 0)) {
449 		const struct byte_bpb33* b33 = reinterpret_cast<const byte_bpb33*>(bootsector->bs33.bsBPB);
450 		if (volume->pm_Sectors == 0) {
451 			INFORM("sector count missing\n");
452 			status = B_BAD_VALUE;
453 		} else {
454 			volume->pm_HugeSectors = volume->pm_Sectors;
455 				// The driver relies on pm_HugeSectors containing the sector count,
456 				// regardless of which BPB field the sector count is stored in.
457 			volume->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
458 			volume->pm_flags |= MSDOSFS_FATMIRROR;
459 		}
460 	} else if (FAT12(volume) != 0 || FAT16(volume) != 0) {
461 		const struct byte_bpb50* b50 = reinterpret_cast<const byte_bpb50*>(bootsector->bs50.bsBPB);
462 		if (volume->pm_Sectors == 0)
463 			volume->pm_HugeSectors = getulong(b50->bpbHugeSectors);
464 		else
465 			volume->pm_HugeSectors = volume->pm_Sectors;
466 		volume->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
467 		volume->pm_flags |= MSDOSFS_FATMIRROR;
468 		if (FAT16(volume) && volume->pm_mountp != NULL)
469 			fix_zip(b50, volume);
470 	} else if (FAT32(volume) != 0) {
471 		const struct byte_bpb710* b710
472 			= reinterpret_cast<const byte_bpb710*>(bootsector->bs710.bsBPB);
473 		volume->pm_HiddenSects = getulong(b710->bpbHiddenSecs);
474 		volume->pm_HugeSectors = getulong(b710->bpbHugeSectors);
475 		volume->pm_FATsecs = getulong(b710->bpbBigFATsecs);
476 			// overwrite the contents of the 16-bit FATsecs BPB field, which would be 0 for FAT32
477 		if ((getushort(b710->bpbExtFlags) & FATMIRROR) != 0)
478 			volume->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM;
479 		else
480 			volume->pm_flags |= MSDOSFS_FATMIRROR;
481 		volume->pm_rootdirblk = getulong(b710->bpbRootClust);
482 		volume->pm_fsinfo = getushort(b710->bpbFSInfo);
483 	} else {
484 		status = B_UNSUPPORTED;
485 	}
486 
487 	if (status != B_OK)
488 		return status;
489 
490 	// check the validity of some type-specific fields
491 	if (FAT12(volume) || FAT16(volume)) {
492 		if ((volume->pm_RootDirEnts * 32) % volume->pm_BytesPerSec != 0) {
493 			INFORM("invalid number of root dir entries\n");
494 			status = B_BAD_VALUE;
495 		}
496 	} else if (FAT32(volume)) {
497 		const struct byte_bpb710* b710
498 			= reinterpret_cast<const byte_bpb710*>(bootsector->bs710.bsBPB);
499 		if (getushort(b710->bpbSectors) != 0 || getushort(b710->bpbFSVers) != 0
500 			|| getushort(b710->bpbRootDirEnts) != 0 || getushort(b710->bpbFATsecs) != 0) {
501 			INFORM("bad FAT32 filesystem\n");
502 			status = B_BAD_VALUE;
503 		}
504 	}
505 	if ((off_t) volume->pm_HugeSectors * volume->pm_BytesPerSec < volume->pm_HugeSectors) {
506 		INFORM("sectors overflow\n");
507 		status = B_BAD_VALUE;
508 	}
509 	if (volume->pm_mountp != NULL) {
510 		if (static_cast<off_t>(volume->pm_HugeSectors) * volume->pm_BytesPerSec
511 			> volume->pm_dev->si_mediasize) {
512 			INFORM("sectors past end of vol:  %u sectors on a %" B_PRIdOFF "-byte volume\n",
513 				volume->pm_HugeSectors, volume->pm_dev->si_mediasize);
514 			status = B_BAD_VALUE;
515 		}
516 	}
517 
518 	return status;
519 }
520 
521 
522 /*! Zip disks that were formatted at iomega have an incorrect number of sectors.
523 	They say that they have 196576 sectors but they really only have 196192.
524 	This check is a work-around for their brain-deadness.
525 	@pre volume->pm_HugeSectors and volume->pm_dev have been initialized.
526 */
527 void
528 fix_zip(const byte_bpb50* bpb, msdosfsmount* volume)
529 {
530 	device_geometry* geo = volume->pm_dev->si_geometry;
531 
532 	if (geo != NULL) {
533 		// the BPB values that are characteristic of these disks
534 		unsigned char bogusZipData[] = {0x00, 0x02, 0x04, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00,
535 			0xf8, 0xc0, 0x00, 0x20, 0x00, 0x40, 0x00, 0x20, 0x00, 0x00};
536 
537 		if (memcmp(bpb, bogusZipData, sizeof(bogusZipData)) == 0 && volume->pm_HugeSectors == 196576
538 			&& (static_cast<off_t>(geo->sectors_per_track) * static_cast<off_t>(geo->cylinder_count)
539 				   * static_cast<off_t>(geo->head_count))
540 				== 196192) {
541 			volume->pm_HugeSectors = 196192;
542 		}
543 	}
544 
545 	return;
546 }
547 
548 
549 /*! Collect data from the fsinfo sector, if applicable.
550 	@pre devNode and the block cache are initialized. The volume is still in the process
551 	of being mounted, so we have exclusive access to the sector (there is no need for a read lock).
552 */
553 status_t
554 read_fsinfo(msdosfsmount* volume, const vnode* devNode)
555 {
556 	status_t status = B_OK;
557 
558 	if (FAT32(volume) != 0) {
559 		const uint8* buffer;
560 		const struct fsinfo* fsInfo;
561 
562 		status = block_cache_get_etc(volume->pm_mountp->mnt_cache, volume->pm_fsinfo,
563 			reinterpret_cast<const void**>(&buffer));
564 		if (status != B_OK)
565 			RETURN_ERROR(status);
566 
567 		fsInfo = reinterpret_cast<const fsinfo*>(buffer);
568 		if (memcmp(fsInfo->fsisig1, "RRaA", 4) == 0 && memcmp(fsInfo->fsisig2, "rrAa", 4) == 0
569 			&& memcmp(fsInfo->fsisig3, "\0\0\125\252", 4) == 0) {
570 			volume->pm_nxtfree = getulong(fsInfo->fsinxtfree);
571 			if (volume->pm_nxtfree > volume->pm_maxcluster)
572 				volume->pm_nxtfree = CLUST_FIRST;
573 			// fillinusemap will populate pm_freeclustercount
574 		} else {
575 			INFORM("fsinfo block has invalid magic number\n");
576 			status = B_BAD_VALUE;
577 			volume->pm_fsinfo = 0;
578 		}
579 
580 		block_cache_put(volume->pm_mountp->mnt_cache, volume->pm_fsinfo);
581 	}
582 
583 	/*
584 	 * Finish initializing pmp->pm_nxtfree (just in case the first few
585 	 * sectors aren't properly reserved in the FAT). This completes
586 	 * the fixup for fp->fsinxtfree, and fixes up the zero-initialized
587 	 * value if there is no fsinfo. We will use pmp->pm_nxtfree
588 	 * internally even if there is no fsinfo.
589 	 */
590 	if (volume->pm_nxtfree < CLUST_FIRST)
591 		volume->pm_nxtfree = CLUST_FIRST;
592 
593 	return status;
594 }
595 
596 
597 /*! Update the fsinfo sector, if applicable.
598 	@pre the block cache is initialized.
599 */
600 status_t
601 write_fsinfo(msdosfsmount* volume)
602 {
603 	if (volume->pm_fsinfo == 0 || FAT32(volume) == false || (volume->pm_flags & MSDOSFS_FSIMOD) == 0
604 		|| (volume->pm_flags & MSDOSFSMNT_RONLY) != 0) {
605 		return B_OK;
606 	}
607 
608 	void* buffer = block_cache_get_writable(volume->pm_mountp->mnt_cache, volume->pm_fsinfo, -1);
609 	if (buffer == NULL)
610 		RETURN_ERROR(B_ERROR);
611 
612 	struct fsinfo* fsInfo = reinterpret_cast<struct fsinfo*>(buffer);
613 	if (memcmp(fsInfo->fsisig1, "RRaA", 4) != 0 || memcmp(fsInfo->fsisig2, "rrAa", 4) != 0
614 		|| memcmp(fsInfo->fsisig3, "\0\0\125\252", 4) != 0) {
615 		block_cache_set_dirty(volume->pm_mountp->mnt_cache, volume->pm_fsinfo, false, -1);
616 		block_cache_put(volume->pm_mountp->mnt_cache, volume->pm_fsinfo);
617 		RETURN_ERROR(B_ERROR);
618 	}
619 
620 	putulong(fsInfo->fsinfree, volume->pm_freeclustercount);
621 	putulong(fsInfo->fsinxtfree, volume->pm_nxtfree);
622 	volume->pm_flags &= ~MSDOSFS_FSIMOD;
623 
624 	block_cache_put(volume->pm_mountp->mnt_cache, volume->pm_fsinfo);
625 
626 	return B_OK;
627 }
628 
629 
630 /*! Perform sanity checks on the FATs, in addition to the media descriptor check of the active FAT
631 	that is done by fillinusemap().
632 
633 */
634 status_t
635 check_fat(const msdosfsmount* volume)
636 {
637 	uint8 fatBuffer[512];
638 	uint8 mirrorBuffer[512];
639 
640 	// For small FATs, check whether each FAT mirror matches the active FAT.
641 	// For large FATs, that takes too long, so just check the first block of each FAT.
642 	uint32 checkBlocks = volume->pm_FATsecs > 4096 ? 1 : volume->pm_FATsecs;
643 	PRINT("check_fat checking %" B_PRIu32 " blocks\n", checkBlocks);
644 
645 	// for each block
646 	for (uint32 i = 0; i < checkBlocks; ++i) {
647 		// read a block from the first/active fat
648 		uint32 resBlocks = volume->pm_ResSectors * volume->pm_BlkPerSec;
649 		off_t position = 512 * (resBlocks + volume->pm_curfat * volume->pm_FATsecs + i);
650 		ssize_t bytes_read
651 			= read_pos(volume->pm_dev->si_fd, position, reinterpret_cast<void*>(fatBuffer), 0x200);
652 		if (bytes_read != 0x200)
653 			RETURN_ERROR(B_IO_ERROR);
654 
655 		// for each mirror
656 		for (uint32 j = 0; j < volume->pm_FATs; ++j) {
657 			if (j == volume->pm_curfat)
658 				continue;
659 			position = 512 * (resBlocks + volume->pm_FATsecs * j + i);
660 			bytes_read = read_pos(volume->pm_dev->si_fd, position,
661 				reinterpret_cast<void*>(mirrorBuffer), 0x200);
662 			if (bytes_read != 0x200)
663 				RETURN_ERROR(B_IO_ERROR);
664 
665 			if (i == 0 && mirrorBuffer[0] != volume->pm_Media) {
666 				INFORM("dosfs error: media descriptor mismatch in fat # "
667 					   "%" B_PRIu32 " (%" B_PRIu8 " != %" B_PRIu8 ")\n",
668 					j, mirrorBuffer[0], volume->pm_Media);
669 				return B_BAD_VALUE;
670 			}
671 
672 			// checking for exact matches of fats is too
673 			// restrictive; allow these to go through in
674 			// case the fat is corrupted for some reason
675 			if (memcmp(fatBuffer, mirrorBuffer, 0x200)) {
676 				INFORM("FAT %" B_PRIu32 " doesn't match active FAT (%u) on %s.\n"
677 					   "Install dosfstools and use fsck.fat to inspect %s.\n",
678 					j, volume->pm_curfat, volume->pm_dev->si_device, volume->pm_dev->si_device);
679 			}
680 		}
681 	}
682 
683 	return B_OK;
684 }
685 
686 
687 /*! Traverse n fat entries.
688 	E.g. for n = 1, return the cluster that is next in the chain after 'cluster'
689 	If 'cluster' is the last in the chain, returns the last-cluster value.
690 */
691 uint32
692 get_nth_fat_entry(msdosfsmount* fatVolume, uint32 cluster, uint32 n)
693 {
694 	int status = 0;
695 
696 	ReadLocker locker(fatVolume->pm_fatlock.haikuRW);
697 
698 	u_long resultCluster = static_cast<u_long>(cluster);
699 
700 	while (n--) {
701 		status = fatentry(FAT_GET, fatVolume, resultCluster, &resultCluster, 0);
702 		if (status != 0) {
703 			REPORT_ERROR(B_FROM_POSIX_ERROR(status));
704 			break;
705 		}
706 		ASSERT(IS_DATA_CLUSTER(resultCluster));
707 	}
708 
709 	return static_cast<uint32>(resultCluster);
710 }
711 
712 
713 status_t
714 init_csi(msdosfsmount* fatVolume, uint32 cluster, uint32 sector, struct csi* csi)
715 {
716 	int ret;
717 	if ((ret = validate_cs(fatVolume, cluster, sector)) != B_OK)
718 		return ret;
719 
720 	csi->fatVolume = fatVolume;
721 	csi->cluster = cluster;
722 	csi->sector = sector;
723 
724 	return B_OK;
725 }
726 
727 
728 status_t
729 validate_cs(msdosfsmount* fatVolume, uint32 cluster, uint32 sector)
730 {
731 	if (cluster == MSDOSFSROOT) {
732 		if (sector >= fatVolume->pm_rootdirsize / fatVolume->pm_BlkPerSec)
733 			return B_ERROR;
734 		return B_OK;
735 	}
736 
737 	if (sector >= SECTORS_PER_CLUSTER(fatVolume)) {
738 		INFORM("validate_cs:  invalid sector (%" B_PRIu32 ")\n", sector);
739 		return B_ERROR;
740 	}
741 
742 	if (!IS_DATA_CLUSTER(cluster)) {
743 		INFORM("validate_cs:  cluster %" B_PRIu32 " compared to max %lu\n", cluster,
744 			fatVolume->pm_maxcluster);
745 		return B_ERROR;
746 	}
747 
748 	return B_OK;
749 }
750 
751 
752 /*! Return the filesystem-relative sector number that corresponds to the argument.
753 
754 */
755 off_t
756 fs_sector(struct csi* csi)
757 {
758 	ASSERT(validate_cs(csi->fatVolume, csi->cluster, csi->sector) == 0);
759 
760 	if (csi->cluster == MSDOSFSROOT) {
761 		return static_cast<off_t>(csi->fatVolume->pm_rootdirblk) / csi->fatVolume->pm_BlkPerSec
762 			+ csi->sector;
763 	}
764 
765 	off_t clusterStart
766 		= cntobn(csi->fatVolume, static_cast<off_t>(csi->cluster)) / csi->fatVolume->pm_BlkPerSec;
767 
768 	return clusterStart + csi->sector;
769 }
770 
771 
772 /*! Advance csi by the indicated number of sectors.
773 	If it's not possible to advance this many sectors the function returns B_ERROR.
774 */
775 status_t
776 iter_csi(struct csi* csi, int sectors)
777 {
778 	if (csi->sector == 0xffff) // check if already at end of chain
779 		return B_ERROR;
780 
781 	if (sectors < 0)
782 		return B_BAD_VALUE;
783 
784 	if (sectors == 0)
785 		return B_OK;
786 
787 	if (csi->cluster == MSDOSFSROOT) {
788 		csi->sector += sectors;
789 		if (csi->sector < csi->fatVolume->pm_rootdirsize / csi->fatVolume->pm_BlkPerSec)
790 			return B_OK;
791 	} else {
792 		u_long secPerClust = SECTORS_PER_CLUSTER(csi->fatVolume);
793 		csi->sector += sectors;
794 		if (csi->sector < secPerClust)
795 			return B_OK;
796 		csi->cluster = get_nth_fat_entry(csi->fatVolume, csi->cluster, csi->sector / secPerClust);
797 
798 		if (MSDOSFSEOF(csi->fatVolume, csi->cluster)) {
799 			csi->sector = 0xffff;
800 			return B_ERROR;
801 		}
802 
803 		if (vIS_DATA_CLUSTER(csi->fatVolume, csi->cluster)) {
804 			csi->sector %= SECTORS_PER_CLUSTER(csi->fatVolume);
805 			return B_OK;
806 		}
807 	}
808 
809 	csi->sector = 0xffff;
810 
811 	return B_ERROR;
812 }
813 
814 
815 NodePutter::NodePutter()
816 	:
817 	fNode(NULL)
818 {
819 }
820 
821 
822 NodePutter::NodePutter(vnode* node)
823 	:
824 	fNode(node)
825 {
826 }
827 
828 
829 NodePutter::~NodePutter()
830 {
831 	Put();
832 }
833 
834 
835 void
836 NodePutter::SetTo(vnode* node)
837 {
838 	fNode = node;
839 }
840 
841 
842 void
843 NodePutter::Put()
844 {
845 	if (fNode != NULL) {
846 		fs_volume* volume = fNode->v_mount->mnt_fsvolume;
847 		denode* fatNode = reinterpret_cast<denode*>(fNode->v_data);
848 		ino_t ino = static_cast<ino_t>(fatNode->de_inode);
849 		status_t status = put_vnode(volume, ino);
850 		if (status != B_OK)
851 			REPORT_ERROR(status);
852 		fNode = NULL;
853 	}
854 }
855 
856 
857 /*! Given a tentative inode number based on direntry location, set it the final inode number.
858 	The final number will be different if the directory entry slot of a deleted or renamed file
859 	is re-used. Also add the node to the vcache if it isn't already added. Unlike the original
860 	Haiku FAT driver, each node is added to the vcache (not just those with artificial ID #s).
861 */
862 status_t
863 assign_inode(mount* volume, ino_t* inode)
864 {
865 	ino_t location = *inode;
866 	status_t result = B_OK;
867 
868 	if (*inode == root_inode(reinterpret_cast<msdosfsmount*>(volume->mnt_data)))
869 		return B_OK;
870 
871 	// populate finalInode with the ID of the node with this direntry location, if
872 	// such a mapping exists in the vcache
873 	ino_t finalInode = 0;
874 	result = vcache_loc_to_vnid(volume, location, &finalInode);
875 	if (result == ENOENT) {
876 		// Search again, this time using the location as an ID.
877 		// If an entry is found, then we know that this location is being used
878 		// as an ID by some (other) node.
879 		if (find_vnid_in_vcache(volume, location) == B_OK) {
880 			// The location cannot be used as an ID because it is already in use.
881 			// Assign an artificial ID.
882 			finalInode = generate_unique_vnid(volume);
883 			if ((result = add_to_vcache(volume, finalInode, location)) < 0)
884 				RETURN_ERROR(result);
885 		} else {
886 			// otherwise we are free to use it
887 			finalInode = location;
888 			if ((result = add_to_vcache(volume, finalInode, location)) < 0)
889 				RETURN_ERROR(result);
890 		}
891 	} else if (result != B_OK) {
892 		RETURN_ERROR(result);
893 	}
894 
895 	PRINT("assign_inode in:  %" B_PRIdINO ", out:  %" B_PRIdINO "\n", *inode, finalInode);
896 	*inode = finalInode;
897 
898 	return B_OK;
899 }
900 
901 
902 status_t
903 assign_inode_and_get(mount* bsdVolume, daddr_t cluster, u_long offset, vnode** bsdNode)
904 {
905 	msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
906 	ino_t ino = DETOI(fatVolume, cluster, offset);
907 	status_t status = assign_inode(bsdVolume, &ino);
908 	if (status != B_OK)
909 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
910 	fs_volume* fsVolume = bsdVolume->mnt_fsvolume;
911 	status = get_vnode(fsVolume, ino, reinterpret_cast<void**>(bsdNode));
912 	if (status != B_OK)
913 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
914 	return B_OK;
915 }
916 
917 
918 /*! Convert inode into direntry location.
919 	@param vnid A final inode number (possibly location-based, possibly artificial).
920 	@post dirclust and diroffset specify the location of the direntry associated with vnid. If the
921 	location is in the (FAT12/16) root directory, diroffset is relative to the start of the root
922 	directory; otherwise it is cluster-relative.
923 */
924 status_t
925 get_location(mount* bsdVolume, ino_t vnid, u_long* dirclust, u_long* diroffset)
926 {
927 	msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
928 	ino_t location = -1;
929 
930 	vcache_vnid_to_loc(bsdVolume, vnid, &location);
931 
932 	if (dirclust != NULL && diroffset != NULL) {
933 		// do the reverse of DETOI
934 		if (static_cast<unsigned long>(location) < fatVolume->pm_RootDirEnts) {
935 			// this is a regular file in a fixed root directory
936 			*dirclust = MSDOSFSROOT;
937 			*diroffset = location << 5;
938 		} else {
939 			location -= fatVolume->pm_RootDirEnts;
940 			location <<= 5;
941 			*dirclust = (location / fatVolume->pm_bpcluster) + 2;
942 			*diroffset = location % fatVolume->pm_bpcluster;
943 		}
944 	}
945 
946 	return B_OK;
947 }
948 
949 
950 bool
951 node_exists(mount* bsdVolume, uint64_t inode)
952 {
953 	bool constructed = false;
954 	vcache_get_constructed(bsdVolume, inode, &constructed);
955 
956 	return constructed;
957 }
958 
959 
960 /*! Analagous to FreeBSD's vfs_timestamp, but returns the local time instead of GMT.
961 
962 */
963 void
964 timestamp_local(timespec* tsp)
965 {
966 	bigtime_t usecs = real_time_clock_usecs();
967 	tsp->tv_sec = (usecs / 1000000LL);
968 	tsp->tv_nsec = (usecs - tsp->tv_sec * 1000000LL) * 1000LL;
969 
970 	return;
971 }
972 
973 
974 void
975 local_to_GMT(const timespec* tspLocal, timespec* tspGMT)
976 {
977 	*tspGMT = *tspLocal;
978 
979 	int32 offset = 0;
980 
981 #ifdef _KERNEL_MODE
982 	offset = static_cast<int32>(get_timezone_offset());
983 #elif defined USER
984 	time_t localTime;
985 	time(&localTime);
986 
987 	struct tm localTm;
988 	localtime_r(&localTime, &localTm);
989 	offset = localTm.tm_gmtoff;
990 #endif
991 
992 	tspGMT->tv_sec -= offset;
993 
994 	return;
995 }
996 
997 
998 /*! Allocate and insert a struct buf into a buffer list.
999 	@param deviceNode The node that represents the mounted device, whose v_bufobj member is to be
1000 	updated.
1001 	@param size The required size of buf::b_data.
1002 	@pre The msdosfsmount has been initialized and v_bufobj.bo_lock is write locked.
1003 	@post A new buf is inserted at the head of the bufobj SLIST that corresponds to 'size.'  C-style
1004 	allocation so the buf can be freed in C code.
1005 */
1006 status_t
1007 slist_insert_buf(vnode* deviceNode, size_t size)
1008 {
1009 	buf_list* list = NULL;
1010 	uint32* count = NULL;
1011 	msdosfsmount* fatVolume
1012 		= reinterpret_cast<msdosfsmount*>(deviceNode->v_rdev->si_mountpt->mnt_data);
1013 
1014 	if (size == 0) {
1015 		list = &deviceNode->v_bufobj.bo_emptybufs;
1016 		count = &deviceNode->v_bufobj.bo_empties;
1017 	} else if (size == fatVolume->pm_fatblocksize) {
1018 		list = &deviceNode->v_bufobj.bo_fatbufs;
1019 		count = &deviceNode->v_bufobj.bo_fatblocks;
1020 	} else if (size == fatVolume->pm_bpcluster) {
1021 		list = &deviceNode->v_bufobj.bo_clusterbufs;
1022 		count = &deviceNode->v_bufobj.bo_clusters;
1023 	} else {
1024 		return B_UNSUPPORTED;
1025 	}
1026 
1027 	buf* newBuf = reinterpret_cast<buf*>(calloc(1, sizeof(buf)));
1028 	if (newBuf == NULL)
1029 		return B_NO_MEMORY;
1030 	if (size != 0) {
1031 		newBuf->b_data = reinterpret_cast<caddr_t>(calloc(size, sizeof(char)));
1032 		if (newBuf->b_data == NULL) {
1033 			free(newBuf);
1034 			return B_NO_MEMORY;
1035 		}
1036 	}
1037 	newBuf->b_bufsize = size;
1038 	// The other members of newBuf will be initialized by getblkx
1039 
1040 	SLIST_INSERT_HEAD(list, newBuf, link);
1041 	(*count)++;
1042 
1043 	return B_OK;
1044 }
1045 
1046 
1047 status_t
1048 fill_gap_with_zeros(vnode* bsdNode, off_t pos, off_t newSize)
1049 {
1050 	while (pos < newSize) {
1051 		size_t size;
1052 		if (newSize > pos + 1024 * 1024 * 1024)
1053 			size = 1024 * 1024 * 1024;
1054 		else
1055 			size = newSize - pos;
1056 
1057 		status_t status = file_cache_write(bsdNode->v_cache, NULL, pos, NULL, &size);
1058 		if (status < B_OK)
1059 			return status;
1060 
1061 		pos += size;
1062 	}
1063 
1064 	return B_OK;
1065 }
1066 
1067 
1068 /*! Sync the block cache blocks that hold the data of a directory file.
1069 
1070 */
1071 status_t
1072 sync_clusters(vnode* bsdNode)
1073 {
1074 	mount* bsdVolume = bsdNode->v_mount;
1075 	denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
1076 	msdosfsmount* fatVolume = fatNode->de_pmp;
1077 	status_t status = B_OK;
1078 
1079 	ASSERT(bsdNode->v_type == VDIR);
1080 
1081 	u_long cluster = fatNode->de_dirclust;
1082 
1083 	if (cluster == MSDOSFSROOT) {
1084 		status = block_cache_sync_etc(bsdVolume->mnt_cache, fatVolume->pm_rootdirblk,
1085 			fatVolume->pm_rootdirsize);
1086 	} else {
1087 		status_t fatStatus = B_OK;
1088 		while ((IS_DATA_CLUSTER(cluster)) && status == B_OK && fatStatus == B_OK) {
1089 			status = block_cache_sync_etc(bsdVolume->mnt_cache, de_cn2bn(fatVolume, cluster),
1090 				BLOCKS_PER_CLUSTER(fatVolume));
1091 			fatStatus = B_FROM_POSIX_ERROR(fatentry(FAT_GET, fatVolume, cluster, &cluster, 0));
1092 		}
1093 		if (fatStatus != B_OK)
1094 			REPORT_ERROR(fatStatus);
1095 	}
1096 
1097 	RETURN_ERROR(status);
1098 }
1099 
1100 
1101 /*! Discard the block cache blocks that hold a directory's data. Has no effect on the root
1102 	directory in FAT12/16; those blocks will be discarded when the volume is unmounted.
1103 */
1104 status_t
1105 discard_clusters(vnode* bsdNode, off_t newLength)
1106 {
1107 	mount* bsdVolume = bsdNode->v_mount;
1108 	denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
1109 	msdosfsmount* fatVolume = fatNode->de_pmp;
1110 	status_t status = B_OK;
1111 
1112 	ASSERT(bsdNode->v_type == VDIR);
1113 
1114 	// if we arrived here from detrunc, de_StartCluster has already been reset.
1115 	u_long cluster = fatNode->de_dirclust;
1116 
1117 	// Typically we are discarding all clusters associated with a directory. However, in
1118 	// the case of an error, the driver might shrink a directory to undo an attempted expansion,
1119 	// as in createde.
1120 	for (uint32 skip = howmany(newLength, fatVolume->pm_bpcluster); skip > 0 && status == B_OK;
1121 		skip--) {
1122 		status = B_FROM_POSIX_ERROR(fatentry(FAT_GET, fatVolume, cluster, &cluster, 0));
1123 	}
1124 
1125 	while ((IS_DATA_CLUSTER(cluster)) && status == B_OK) {
1126 		block_cache_discard(bsdVolume->mnt_cache, de_cn2bn(fatVolume, cluster),
1127 			BLOCKS_PER_CLUSTER(fatVolume));
1128 		status = B_FROM_POSIX_ERROR(fatentry(FAT_GET, fatVolume, cluster, &cluster, 0));
1129 	}
1130 
1131 	RETURN_ERROR(status);
1132 }
1133 
1134 
1135 /*! For use in the FAT userlandfs module. userlandfs does not provide check_access_permissions().
1136 	Limitation:  ignores permissions granted by the user's group
1137 */
1138 status_t
1139 check_access_permissions_internal(int accessMode, mode_t mode, gid_t nodeGroupID, uid_t nodeUserID)
1140 {
1141 	// get node permissions
1142 	int userPermissions = (mode & S_IRWXU) >> 6;
1143 	int groupPermissions = (mode & S_IRWXG) >> 3;
1144 	int otherPermissions = mode & S_IRWXO;
1145 
1146 	// get the node permissions for this uid/gid
1147 	int permissions = 0;
1148 	uid_t uid = geteuid();
1149 
1150 	if (uid == 0) {
1151 		// user is root
1152 		// root has always read/write permission, but at least one of the
1153 		// X bits must be set for execute permission
1154 		permissions = userPermissions | groupPermissions | otherPermissions | S_IROTH | S_IWOTH;
1155 		if (S_ISDIR(mode))
1156 			permissions |= S_IXOTH;
1157 	} else if (uid == nodeUserID) {
1158 		// user is node owner
1159 		permissions = userPermissions;
1160 	} else {
1161 		// user is one of the others
1162 		permissions = otherPermissions;
1163 	}
1164 
1165 	// userlandfs does not provide is_user_in_group(), so we can't check group permissions
1166 
1167 	return (accessMode & ~permissions) == 0 ? B_OK : B_PERMISSION_DENIED;
1168 }
1169 
1170 
1171 /*! Populates file mode bits only (not file type bits).
1172 
1173 */
1174 void
1175 mode_bits(const vnode* bsdNode, mode_t* mode)
1176 {
1177 	denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
1178 	msdosfsmount* fatVolume = fatNode->de_pmp;
1179 
1180 	*mode = S_IRUSR | S_IRGRP | S_IROTH;
1181 
1182 	if ((fatNode->de_Attributes & ATTR_READONLY) == 0)
1183 		*mode |= S_IWUSR;
1184 
1185 	// In FAT, there is no place to store an executable flag on disk. FreeBSD makes all FAT files
1186 	// executable, but Tracker will complain if, for example, a text file is executable.
1187 	// To avoid that, we go by the MIME type.
1188 	if (bsdNode->v_type == VDIR
1189 		|| (bsdNode->v_type == VREG && bsdNode->v_mime != NULL
1190 			&& strcmp(bsdNode->v_mime, "application/octet-stream") == 0)) {
1191 		*mode |= S_IXUSR | S_IXGRP | S_IXOTH;
1192 	}
1193 
1194 	*mode &= (bsdNode->v_type == VDIR) ? fatVolume->pm_dirmask : fatVolume->pm_mask;
1195 
1196 	return;
1197 }
1198 
1199 
1200 /*! Set the mime type of a node; has no effect in fat_shell.
1201 	@param update True if this is an update to a pre-existing mime setting.
1202 */
1203 status_t
1204 set_mime_type(vnode* bsdNode, bool update)
1205 {
1206 #ifndef FS_SHELL
1207 	mount* bsdVolume = reinterpret_cast<mount*>(bsdNode->v_mount);
1208 	denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
1209 	msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(fatNode->de_pmp);
1210 
1211 	if (bsdNode->v_type == VREG) {
1212 		char unixShortname[SHORTNAME_CSTRING + 1];
1213 			// +1 for the period added by dos2unixfn
1214 		dos2unixfn(fatNode->de_Name, reinterpret_cast<u_char*>(unixShortname), 0, fatVolume);
1215 
1216 		set_mime(&bsdNode->v_mime, unixShortname);
1217 
1218 		notify_attribute_changed(bsdVolume->mnt_fsvolume->id, bsdNode->v_parent, fatNode->de_inode,
1219 			"BEOS:TYPE", update ? B_ATTR_CHANGED : B_ATTR_CREATED);
1220 	}
1221 #endif // FS_SHELL
1222 
1223 	return B_OK;
1224 }
1225 
1226 
1227 /*! Set a user-specified code page for translating FAT short filenames in UserlandFS.
1228 	The FAT filesystem assigns both a short name and a long name to each file/directory.
1229 	The short names are encoded in an 8-bit or DBCS OEM code page, aka DOS code page.
1230 	Short names must be unique within a directory.
1231 	If the user assigns a name containing a character not in the OEM code page, then
1232 	the short name will substitute an underscore character.
1233 	Users only see the long name, but collisions between short entry names are more likely if
1234 	the OEM code page is not suitable for the user's language.
1235 	FAT FS will attempt to resolve collisions by adding a generation number to the new
1236 	short filename (also invisible to the user).
1237 	Many code pages are supported by libiconv, which we use in the the userlandfs module.
1238 	libiconv is not available in the FS shell or the kernel; in those cases the driver defaults to
1239 	an internal copy of code page 850.
1240 */
1241 status_t
1242 iconv_init(msdosfsmount* fatVolume, const char* oemPreference)
1243 {
1244 	fatVolume->pm_u2w = NULL;
1245 	fatVolume->pm_w2u = NULL;
1246 	fatVolume->pm_u2d = NULL;
1247 	fatVolume->pm_d2u = NULL;
1248 
1249 #ifndef USER
1250 	return B_OK;
1251 #else
1252 	if ((fatVolume->pm_flags & MSDOSFSMNT_KICONV) == 0 || strcmp(oemPreference, "") == 0)
1253 		return B_OK;
1254 
1255 	msdosfs_iconv = new(std::nothrow) iconv_functions;
1256 		// fills in for FreeBSD's VFS_DECLARE_ICONV macro
1257 	if (msdosfs_iconv == NULL)
1258 		RETURN_ERROR(B_NO_MEMORY);
1259 	ObjectDeleter<iconv_functions> deleter(msdosfs_iconv);
1260 
1261 	msdosfs_iconv->open = fat_iconv_open;
1262 	msdosfs_iconv->close = fat_iconv_close;
1263 	msdosfs_iconv->conv = iconv_conv;
1264 	msdosfs_iconv->convchr = iconv_convchr;
1265 	msdosfs_iconv->convchr_case = iconv_convchr_case;
1266 
1267 	PRINT("setting code page to %s\n", oemPreference);
1268 
1269 	const char* haiku = "UTF-8";
1270 	const char* windows = "UCS-2";
1271 	const char* dos = oemPreference;
1272 
1273 	status_t status = B_OK;
1274 	status = B_FROM_POSIX_ERROR(msdosfs_iconv->open(windows, haiku, &fatVolume->pm_u2w));
1275 	if (status != B_OK)
1276 		RETURN_ERROR(errno);
1277 
1278 	status = B_FROM_POSIX_ERROR(msdosfs_iconv->open(haiku, windows, &fatVolume->pm_w2u));
1279 	if (status != B_OK)
1280 		RETURN_ERROR(errno);
1281 
1282 	status = B_FROM_POSIX_ERROR(msdosfs_iconv->open(dos, haiku, &fatVolume->pm_u2d));
1283 	if (status != B_OK)
1284 		RETURN_ERROR(errno);
1285 
1286 	status = B_FROM_POSIX_ERROR(msdosfs_iconv->open(haiku, dos, &fatVolume->pm_d2u));
1287 	if (status != B_OK)
1288 		RETURN_ERROR(errno);
1289 
1290 	deleter.Detach();
1291 
1292 	return B_OK;
1293 #endif // USER
1294 }
1295