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