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