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