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