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 #ifdef FS_SHELL
59 #include "fssh_api_wrapper.h"
60 #else // !FS_SHELL
61 #include <dirent.h>
62 #include <malloc.h>
63 #include <new>
64 #include <stdlib.h>
65 #endif // !FS_SHELL
66
67 #ifndef FS_SHELL
68 #include <NodeMonitor.h>
69 #include <OS.h>
70 #include <TypeConstants.h>
71 #include <driver_settings.h>
72 #include <fs_info.h>
73 #include <fs_interface.h>
74 #include <fs_volume.h>
75 #include <io_requests.h>
76 #endif // !FS_SHELL
77
78 #if defined USER && __GNUC__ == 2
79 // required for fs_ops_support.h
80 #define alignof(type) __alignof__(type)
81 #endif // USER && __GNUC__ == 2
82 #include <fs_ops_support.h>
83 #ifdef FS_SHELL
84 #include "fssh_auto_deleter.h"
85 #include "syscalls.h"
86 #else // !FS_SHELL
87 #include <AutoDeleter.h>
88 #include <arch_vm.h>
89 #include <kernel.h>
90 #include <syscalls.h>
91 #include <util/AutoLock.h>
92 #include <vfs.h>
93 #endif // !FS_SHELL
94
95 // FreeBSD flag that turns on full implementation of ported code
96 #define _KERNEL
97
98 extern "C"
99 {
100 #include "sys/param.h"
101 #include "sys/buf.h"
102 #include "sys/clock.h"
103 #include "sys/conf.h"
104 #include "sys/iconv.h"
105 #include "sys/mount.h"
106 #include "sys/mutex.h"
107 #include "sys/namei.h"
108 #include "sys/vnode.h"
109
110 #include "fs/msdosfs/bootsect.h"
111 #include "fs/msdosfs/bpb.h"
112 #include "fs/msdosfs/denode.h"
113 #include "fs/msdosfs/direntry.h"
114 #include "fs/msdosfs/fat.h"
115 #include "fs/msdosfs/msdosfsmount.h"
116 }
117
118 #include "debug.h"
119 #include "dosfs.h"
120 #ifdef FS_SHELL
121 #include "fssh_defines.h"
122 #endif // FS_SHELL
123 #include "mkdos.h"
124 #include "support.h"
125 #include "vcache.h"
126
127
128 static status_t iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset,
129 size_t size, struct file_io_vec* vecs, size_t* _count);
130 static status_t iterative_io_finished_hook(void* cookie, io_request* request, status_t status,
131 bool partialTransfer, size_t bytesTransferred);
132
133 static status_t _dosfs_sync(mount* volume, bool data = true);
134 static status_t _dosfs_fsync(vnode* bsdNode);
135 static status_t _dosfs_read_vnode(mount* bsdVolume, const ino_t id, vnode** newNode, bool createFileCache = true);
136
137 static status_t bsd_volume_init(fs_volume* fsVolume, const uint32 flags, mount** volume);
138 status_t bsd_volume_uninit(mount* volume);
139 static status_t bsd_device_init(mount* bsdVolume, const dev_t devID, const char* deviceFile,
140 cdev** bsdDevice, bool* _readOnly);
141 status_t bsd_device_uninit(cdev* device);
142 static status_t dev_bsd_node_init(cdev* bsdDevice, vnode** devNode);
143 status_t dev_bsd_node_uninit(vnode* devNode);
144 static status_t fat_volume_init(vnode* devvp, mount* bsdVolume, const uint64_t fatFlags,
145 const char* oemPref);
146 status_t fat_volume_uninit(msdosfsmount* volume);
147
148
149 typedef struct IdentifyCookie {
150 uint32 fBytesPerSector;
151 uint32 fTotalSectors;
152 char fName[12];
153 } IdentifyCookie;
154
155 typedef struct FileCookie {
156 uint32 fMode; // open mode
157 u_long fLastSize; // file size at last notify_stat_changed call
158 u_short fMtimeAtOpen; // inital modification time
159 u_short fMdateAtOpen; // initial modification date
160 bigtime_t fLastNotification; // time of last notify_stat_changed call
161 } FileCookie;
162
163 typedef struct DirCookie {
164 uint32 fIndex; // read this entry next
165 } DirCookie;
166
167 typedef struct AttrCookie {
168 uint32 fMode; // open mode
169 int32 fType; // attribute type
170 #define FAT_ATTR_MIME 0x1234
171 } AttrCookie;
172
173
174 typedef CObjectDeleter<mount, status_t, &bsd_volume_uninit> StructMountDeleter;
175 typedef CObjectDeleter<cdev, status_t, &bsd_device_uninit> StructCdevDeleter;
176 typedef CObjectDeleter<vnode, status_t, &dev_bsd_node_uninit> DevVnodeDeleter;
177 typedef CObjectDeleter<msdosfsmount, status_t, &fat_volume_uninit> StructMsdosfsmountDeleter;
178
179
180 struct iconv_functions* msdosfs_iconv;
181
182
183 static status_t
dosfs_mount(fs_volume * volume,const char * device,uint32 flags,const char * args,ino_t * _rootVnodeID)184 dosfs_mount(fs_volume* volume, const char* device, uint32 flags, const char* args,
185 ino_t* _rootVnodeID)
186 {
187 #ifdef FS_SHELL
188 FUNCTION_START("device %" B_PRIdDEV "\n", volume->id);
189 #else
190 FUNCTION_START("device %" B_PRIdDEV ", partition %" B_PRId32 "\n", volume->id,
191 volume->partition);
192 #endif
193
194 status_t status = B_OK;
195
196 int opSyncMode = 0;
197 char oemPref[11] = "";
198 void* handle = load_driver_settings("dos");
199 if (handle != NULL) {
200 opSyncMode = strtoul(get_driver_parameter(handle, "op_sync_mode", "0", "0"), NULL, 0);
201 if (opSyncMode < 0 || opSyncMode > 2)
202 opSyncMode = 0;
203
204 strlcpy(oemPref, get_driver_parameter(handle, "OEM_code_page", "", ""), 11);
205
206 unload_driver_settings(handle);
207 }
208
209 uint64 fatFlags = 0;
210 // libiconv support is implemented only for the userlandfs module
211 #ifdef USER
212 fatFlags |= MSDOSFSMNT_KICONV;
213 if (strcmp(oemPref, "") == 0)
214 strlcpy(oemPref, "CP1252", 11);
215 #endif // USER
216
217 // args is a command line option; dosfs doesn't use any so we can ignore it
218
219 bool readOnly = (flags & B_MOUNT_READ_ONLY) != 0;
220 if ((flags & ~B_MOUNT_READ_ONLY) != 0) {
221 INFORM("unsupported mount flag(s) %" B_PRIx32 "\n", (flags & ~B_MOUNT_READ_ONLY));
222 return B_UNSUPPORTED;
223 }
224
225 // Initialize the struct mount, which is an adapted FreeBSD VFS object. It is present in the
226 // port because the ported BSD code relies on it.
227 mount* bsdVolume;
228 status = bsd_volume_init(volume, flags, &bsdVolume);
229 if (status != B_OK)
230 RETURN_ERROR(status);
231 StructMountDeleter bsdVolumeDeleter(bsdVolume);
232
233 // initialize a BSD-style device struct
234 cdev* bsdDevice = NULL;
235 status = bsd_device_init(bsdVolume, volume->id, device, &bsdDevice, &readOnly);
236 if (status != B_OK)
237 RETURN_ERROR(status);
238 StructCdevDeleter bsdDeviceDeleter(bsdDevice);
239
240 if (readOnly == true) {
241 bsdVolume->mnt_flag |= MNT_RDONLY;
242 fatFlags |= MSDOSFSMNT_RONLY;
243 }
244
245 // A shell/FUSE host system might not call dosfs_sync automatically at shutdown/reboot if the
246 // user forgets to unmount a volume, so we always use op sync mode for those targets.
247 #ifdef FS_SHELL
248 opSyncMode = 2;
249 #endif // FS_SHELL
250
251 // see if we need to go into op sync mode
252 switch (opSyncMode) {
253 case 1:
254 if (bsdDevice->si_geometry->removable == false) {
255 // we're not removable, so skip op_sync
256 break;
257 }
258 // supposed to fall through
259
260 case 2:
261 PRINT("mounted with op sync enabled\n");
262 bsdVolume->mnt_flag |= MNT_SYNCHRONOUS;
263 fatFlags |= MSDOSFSMNT_WAITONFAT;
264 break;
265
266 case 0:
267 default:
268 bsdVolume->mnt_flag |= MNT_ASYNC;
269 break;
270 }
271
272 // The driver needs access to a BSD-format vnode representing the device file, which in BSD
273 // would be a vnode on another volume. We manually generate a stand-in.
274 vnode* devNode;
275 status = dev_bsd_node_init(bsdDevice, &devNode);
276 if (status != B_OK)
277 RETURN_ERROR(status);
278 DevVnodeDeleter devVnodeDeleter(devNode);
279
280 // initialize the FAT private volume data
281 status = fat_volume_init(devNode, bsdVolume, fatFlags, oemPref);
282 if (status != B_OK)
283 RETURN_ERROR(status);
284 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
285 StructMsdosfsmountDeleter fatVolumeDeleter(fatVolume);
286
287 // create caches of struct bufs for the driver to use in bread()
288 rw_lock_write_lock(&devNode->v_bufobj.bo_lock.haikuRW);
289 for (uint32 i = 0; i < BUF_CACHE_SIZE; ++i) {
290 status = slist_insert_buf(devNode, fatVolume->pm_bpcluster);
291 if (status != B_OK)
292 RETURN_ERROR(status);
293 status = slist_insert_buf(devNode, fatVolume->pm_fatblocksize);
294 if (status != B_OK)
295 RETURN_ERROR(status);
296 status = slist_insert_buf(devNode, 0);
297 if (status != B_OK)
298 RETURN_ERROR(status);
299 }
300 rw_lock_write_unlock(&devNode->v_bufobj.bo_lock.haikuRW);
301
302 volume->private_volume = bsdVolume;
303 volume->ops = &gFATVolumeOps;
304
305 // publish root vnode
306
307 u_long dirClust = FAT32(fatVolume) == true ? fatVolume->pm_rootdirblk : MSDOSFSROOT;
308 u_long dirOffset = MSDOSFSROOT_OFS;
309 ino_t rootInode = DETOI(fatVolume, dirClust, dirOffset);
310
311 status = add_to_vcache(bsdVolume, rootInode, rootInode);
312 if (status != B_OK)
313 RETURN_ERROR(status);
314
315 vnode* bsdRootNode;
316 status = _dosfs_read_vnode(bsdVolume, rootInode, &bsdRootNode);
317 if (status != B_OK)
318 RETURN_ERROR(status);
319 denode* fatRootNode = reinterpret_cast<denode*>(bsdRootNode->v_data);
320 ASSERT(fatRootNode->de_dirclust == dirClust && fatRootNode->de_diroffset == dirOffset);
321
322 status = publish_vnode(volume, rootInode, bsdRootNode, &gFATVnodeOps, S_IFDIR, 0);
323 if (status != B_OK)
324 RETURN_ERROR(status);
325
326 PRINT("root vnode id = %" B_PRIdINO ", @ %p\n", fatRootNode->de_inode, bsdRootNode);
327
328 *_rootVnodeID = fatRootNode->de_inode;
329
330 #ifdef _KERNEL_MODE
331 // initialize mnt_stat.f_mntonname, for use by msdosfs_integrity_error
332 dev_t mountpt;
333 ino_t mountino;
334 vfs_get_mount_point(fatVolume->pm_dev->si_id, &mountpt, &mountino);
335 vfs_entry_ref_to_path(mountpt, mountino, NULL, true, bsdVolume->mnt_stat.f_mntonname,
336 B_PATH_NAME_LENGTH);
337 #endif // _KERNEL_MODE
338
339 bsdVolumeDeleter.Detach();
340 bsdDeviceDeleter.Detach();
341 devVnodeDeleter.Detach();
342 fatVolumeDeleter.Detach();
343
344 return B_OK;
345 }
346
347
348 static float
dosfs_identify_partition(int fd,partition_data * partition,void ** _cookie)349 dosfs_identify_partition(int fd, partition_data* partition, void** _cookie)
350 {
351 FUNCTION_START("dosfs_identify_partition\n");
352
353 // read in the boot sector
354 uint8 buf[512];
355 if (read_pos(fd, 0, buf, 512) != 512)
356 return -1;
357
358 FatType type;
359 bool dos33;
360 status_t status = check_bootsector(buf, type, dos33);
361 if (status != B_OK)
362 return status;
363
364 // partially set up a msdosfsmount, enough to read the volume label from the root directory
365 msdosfsmount dummyVolume;
366 dummyVolume.pm_mountp = NULL;
367 switch (type) {
368 case fat12:
369 dummyVolume.pm_fatmask = FAT12_MASK;
370 break;
371 case fat16:
372 dummyVolume.pm_fatmask = FAT16_MASK;
373 break;
374 case fat32:
375 dummyVolume.pm_fatmask = FAT32_MASK;
376 break;
377 default:
378 return -1;
379 }
380 status = parse_bpb(&dummyVolume, reinterpret_cast<union bootsector*>(buf), dos33);
381 if (status != B_OK)
382 return status;
383 dummyVolume.pm_BlkPerSec = dummyVolume.pm_BytesPerSec / DEV_BSIZE;
384 dummyVolume.pm_rootdirsize = howmany(dummyVolume.pm_RootDirEnts * sizeof(direntry), DEV_BSIZE);
385 // Will be 0 for a FAT32 volume.
386 dummyVolume.pm_bpcluster
387 = dummyVolume.pm_bpb.bpbSecPerClust * dummyVolume.pm_BlkPerSec * DEV_BSIZE;
388 dummyVolume.pm_bnshift = ffs(DEV_BSIZE) - 1;
389 dummyVolume.pm_fatblk = dummyVolume.pm_ResSectors * dummyVolume.pm_BlkPerSec;
390 if (type == fat32) {
391 // for FAT32, read_label depends on pm_firstcluster
392 dummyVolume.pm_firstcluster
393 = dummyVolume.pm_fatblk + dummyVolume.pm_FATs * dummyVolume.pm_FATsecs;
394 } else {
395 // for FAT12/16, parse_bpb doesn't initialize pm_rootdirblk
396 dummyVolume.pm_rootdirblk
397 = dummyVolume.pm_fatblk + dummyVolume.pm_FATs * dummyVolume.pm_FATsecs;
398 }
399
400 char name[LABEL_CSTRING];
401 strcpy(name, "no name");
402 read_label(&dummyVolume, fd, buf, name);
403
404 IdentifyCookie* cookie = new(std::nothrow) IdentifyCookie;
405 if (!cookie)
406 return -1;
407 cookie->fBytesPerSector = dummyVolume.pm_BytesPerSec;
408 cookie->fTotalSectors = dummyVolume.pm_HugeSectors;
409 strlcpy(cookie->fName, name, 12);
410
411 *_cookie = cookie;
412
413 return 0.8f;
414 }
415
416
417 static status_t
dosfs_scan_partition(int fd,partition_data * partition,void * _cookie)418 dosfs_scan_partition(int fd, partition_data* partition, void* _cookie)
419 {
420 IdentifyCookie* cookie = reinterpret_cast<IdentifyCookie*>(_cookie);
421
422 partition->status = B_PARTITION_VALID;
423 partition->flags |= B_PARTITION_FILE_SYSTEM;
424 partition->content_size = static_cast<off_t>(cookie->fTotalSectors) * cookie->fBytesPerSector;
425 partition->block_size = cookie->fBytesPerSector;
426 partition->content_name = strdup(cookie->fName);
427 if (partition->content_name == NULL)
428 return B_NO_MEMORY;
429
430 return B_OK;
431 }
432
433
434 static void
dosfs_free_identify_partition_cookie(partition_data * partition,void * _cookie)435 dosfs_free_identify_partition_cookie(partition_data* partition, void* _cookie)
436 {
437 delete reinterpret_cast<IdentifyCookie*>(_cookie);
438
439 return;
440 }
441
442
443 static status_t
dosfs_unmount(fs_volume * volume)444 dosfs_unmount(fs_volume* volume)
445 {
446 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
447 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
448 vnode* deviceNode = fatVolume->pm_devvp;
449 cdev* bsdDevice = fatVolume->pm_dev;
450
451 #ifdef FS_SHELL
452 FUNCTION_START("device %" B_PRIdDEV "\n", volume->id);
453 #else
454 FUNCTION_START("device %" B_PRIdDEV ", partition %" B_PRId32 "\n", volume->id,
455 volume->partition);
456 #endif
457
458 status_t status = B_OK;
459 status_t returnStatus = B_OK;
460
461 MutexLocker locker(bsdVolume->mnt_mtx.haikuMutex);
462
463 status = fat_volume_uninit(fatVolume);
464 if (status != B_OK)
465 returnStatus = status;
466
467 // pseudo-BSD layer cleanup
468 status = bsd_device_uninit(bsdDevice);
469 if (status != B_OK)
470 returnStatus = status;
471 status = dev_bsd_node_uninit(deviceNode);
472 if (status != B_OK)
473 returnStatus = status;
474 locker.Unlock();
475 status = bsd_volume_uninit(bsdVolume);
476 if (status != B_OK)
477 returnStatus = status;
478
479 RETURN_ERROR(returnStatus);
480 }
481
482
483 static status_t
dosfs_read_fs_stat(fs_volume * volume,struct fs_info * info)484 dosfs_read_fs_stat(fs_volume* volume, struct fs_info* info)
485 {
486 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
487 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
488 cdev* bsdDevice = fatVolume->pm_dev;
489
490 FUNCTION();
491
492 MutexLocker locker(bsdVolume->mnt_mtx.haikuMutex);
493
494 info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_MIME;
495 if ((bsdVolume->mnt_flag & MNT_RDONLY) != 0)
496 info->flags |= B_FS_IS_READONLY;
497
498 if (bsdDevice->si_geometry->removable == true)
499 info->flags |= B_FS_IS_REMOVABLE;
500
501 info->block_size = fatVolume->pm_bpcluster;
502
503 info->io_size = FAT_IO_SIZE;
504
505 info->total_blocks = fatVolume->pm_maxcluster + 1 - 2;
506 // convert from index to count and adjust for 2 reserved cluster numbers
507
508 info->free_blocks = fatVolume->pm_freeclustercount;
509
510 info->total_nodes = LONGLONG_MAX;
511
512 info->free_nodes = LONGLONG_MAX;
513
514 strlcpy(info->volume_name, fatVolume->pm_dev->si_name, sizeof(info->volume_name));
515
516 strlcpy(info->device_name, fatVolume->pm_dev->si_device, sizeof(info->device_name));
517
518 strlcpy(info->fsh_name, "fat", sizeof(info->fsh_name));
519
520 return B_OK;
521 }
522
523
524 static status_t
dosfs_write_fs_stat(fs_volume * volume,const struct fs_info * info,uint32 mask)525 dosfs_write_fs_stat(fs_volume* volume, const struct fs_info* info, uint32 mask)
526 {
527 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
528 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
529
530 FUNCTION_START("with mask %" B_PRIx32 "\n", mask);
531
532 MutexLocker locker(bsdVolume->mnt_mtx.haikuMutex);
533
534 if ((mask & FS_WRITE_FSINFO_NAME) == 0)
535 return B_OK;
536
537 // if it's a r/o file system, then don't allow volume renaming
538 if ((bsdVolume->mnt_flag & MNT_RDONLY) != 0)
539 return B_READ_ONLY_DEVICE;
540
541 PRINT("wfsstat: setting name to %s\n", info->volume_name);
542 char name[LABEL_CSTRING];
543 strlcpy(name, info->volume_name, LABEL_CSTRING);
544 status_t status = label_to_fat(name);
545 if (status != B_OK)
546 return status;
547
548 // update the BPB, unless the volume is too old to have a label field in the BPB
549 void* blockCache = bsdVolume->mnt_cache;
550 u_char* buffer;
551 status
552 = block_cache_get_writable_etc(blockCache, 0, -1, reinterpret_cast<void**>(&buffer));
553 if (status != B_OK)
554 return status;
555 // check for the extended boot signature
556 uint32 ebsOffset = FAT32(fatVolume) != 0 ? 0x42 : 0x26;
557 uint32 labelOffset = ebsOffset + 5;
558 char* memoryLabel = fatVolume->pm_dev->si_name;
559 if (buffer[ebsOffset] == EXBOOTSIG) {
560 // double check the position by verifying the name presently stored there
561 char bpbLabel[LABEL_CSTRING];
562 memcpy(bpbLabel, buffer + labelOffset, LABEL_LENGTH);
563 label_from_fat(bpbLabel);
564 if (strncmp(bpbLabel, memoryLabel, LABEL_LENGTH) == 0) {
565 memcpy(buffer + labelOffset, name, LABEL_LENGTH);
566 } else {
567 INFORM("wfsstat: BPB position check failed\n");
568 block_cache_set_dirty(blockCache, 0, false, -1);
569 status = B_ERROR;
570 }
571 }
572 block_cache_put(blockCache, 0);
573
574 // update the label file if there is one
575 if (bsdVolume->mnt_volentry >= 0) {
576 uint8* rootDirBuffer;
577 daddr_t rootDirBlock = fatVolume->pm_rootdirblk;
578 if (FAT32(fatVolume) == true)
579 rootDirBlock = cntobn(fatVolume, fatVolume->pm_rootdirblk);
580 daddr_t dirOffset = bsdVolume->mnt_volentry * sizeof(direntry);
581 rootDirBlock += dirOffset / DEV_BSIZE;
582
583 status = block_cache_get_writable_etc(blockCache, rootDirBlock, -1,
584 reinterpret_cast<void**>(&rootDirBuffer));
585 if (status == B_OK) {
586 direntry* label_direntry = reinterpret_cast<direntry*>(rootDirBuffer + dirOffset);
587
588 char rootLabel[LABEL_CSTRING];
589 memcpy(rootLabel, label_direntry->deName, LABEL_LENGTH);
590 label_from_fat(rootLabel);
591 if (strncmp(rootLabel, memoryLabel, LABEL_LENGTH) == 0) {
592 memcpy(label_direntry->deName, name, LABEL_LENGTH);
593 } else {
594 INFORM("wfsstat: root directory position check failed\n");
595 block_cache_set_dirty(blockCache, rootDirBlock, false, -1);
596 status = B_ERROR;
597 }
598 block_cache_put(blockCache, rootDirBlock);
599 }
600 } else {
601 // A future enhancement could be to create a label direntry if none exists already.
602 }
603
604 if (status == B_OK) {
605 memcpy(memoryLabel, name, LABEL_LENGTH);
606 label_from_fat(memoryLabel);
607 }
608
609 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0)
610 _dosfs_sync(bsdVolume, false);
611
612 RETURN_ERROR(status);
613 }
614
615
616 static status_t
dosfs_sync(fs_volume * volume)617 dosfs_sync(fs_volume* volume)
618 {
619 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
620 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
621
622 FUNCTION();
623
624 MutexLocker volumeLocker(bsdVolume->mnt_mtx.haikuMutex);
625 WriteLocker fatLocker(fatVolume->pm_fatlock.haikuRW);
626
627 RETURN_ERROR(_dosfs_sync(bsdVolume));
628 }
629
630
631 /*! If data is true, include regular file data in the sync. Otherwise, only sync directories,
632 the FAT, and, if applicable, the fsinfo sector.
633 */
634 status_t
_dosfs_sync(struct mount * bsdVolume,bool data)635 _dosfs_sync(struct mount* bsdVolume, bool data)
636 {
637 status_t status = B_OK;
638 status_t returnStatus = B_OK;
639
640 status = write_fsinfo(reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data));
641 if (status != B_OK) {
642 REPORT_ERROR(status);
643 returnStatus = status;
644 }
645
646 status = block_cache_sync(bsdVolume->mnt_cache);
647 if (status != B_OK) {
648 REPORT_ERROR(status);
649 returnStatus = status;
650 }
651
652 if (data == true) {
653 status = sync_all_files(bsdVolume);
654 if (status != B_OK) {
655 REPORT_ERROR(status);
656 returnStatus = status;
657 }
658 }
659
660 return returnStatus;
661 }
662
663
664 static status_t
dosfs_read_vnode(fs_volume * volume,ino_t id,fs_vnode * vnode,int * _type,uint32 * _flags,bool reenter)665 dosfs_read_vnode(fs_volume* volume, ino_t id, fs_vnode* vnode, int* _type, uint32* _flags,
666 bool reenter)
667 {
668 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
669 struct vnode* bsdNode;
670
671 FUNCTION_START("id %" B_PRIdINO ", type %d, flags %" B_PRIx32 "\n", id, *_type, *_flags);
672
673 MutexLocker locker(bsdVolume->mnt_mtx.haikuMutex);
674
675 // In case 2 threads are concurrently executing get_vnode() with the same ID, verify
676 // after locking the volume that the node has not been constructed already.
677 if (node_exists(bsdVolume, id) == true)
678 return B_BAD_VALUE;
679
680 status_t status = _dosfs_read_vnode(bsdVolume, id, &bsdNode);
681 if (status != B_OK)
682 RETURN_ERROR(status);
683
684 ASSERT(static_cast<ino_t>(reinterpret_cast<denode*>(bsdNode->v_data)->de_inode) == id);
685
686 vnode->private_node = bsdNode;
687 vnode->ops = &gFATVnodeOps;
688 if (bsdNode->v_type == VDIR)
689 *_type = S_IFDIR;
690 else if (bsdNode->v_type == VREG)
691 *_type = S_IFREG;
692 else
693 panic("dosfs_read_vnode: unknown type\n");
694
695 *_flags = 0;
696
697 return B_OK;
698 }
699
700
701 /*! Can be used internally by the FS to generate a private node.
702
703 */
704 static status_t
_dosfs_read_vnode(mount * bsdVolume,const ino_t id,vnode ** newNode,bool createFileCache)705 _dosfs_read_vnode(mount* bsdVolume, const ino_t id, vnode** newNode, bool createFileCache)
706 {
707 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
708
709 status_t status = B_OK;
710 u_long dirClust, dirOffset;
711 if (id == root_inode(fatVolume)) {
712 dirClust = FAT32(fatVolume) == true ? fatVolume->pm_rootdirblk : MSDOSFSROOT;
713 dirOffset = MSDOSFSROOT_OFS;
714 } else {
715 status = get_location(bsdVolume, id, &dirClust, &dirOffset);
716 if (status != B_OK)
717 return status;
718 }
719
720 denode* fatNode;
721 status = B_FROM_POSIX_ERROR(deget(fatVolume, dirClust, dirOffset, LK_EXCLUSIVE, &fatNode));
722 if (status != B_OK)
723 return status;
724
725 vnode* bsdNode = fatNode->de_vnode;
726 if (bsdNode->v_type == VREG) {
727 status = set_mime_type(bsdNode, false);
728 if (status != B_OK)
729 REPORT_ERROR(status);
730
731 if (createFileCache) {
732 bsdNode->v_cache
733 = file_cache_create(fatVolume->pm_dev->si_id, fatNode->de_inode, fatNode->de_FileSize);
734 bsdNode->v_file_map
735 = file_map_create(fatVolume->pm_dev->si_id, fatNode->de_inode, fatNode->de_FileSize);
736 }
737 }
738
739 // identify the parent directory
740 if (id == root_inode(fatVolume)) {
741 bsdNode->v_parent = id;
742 } else if (bsdNode->v_type == VREG) {
743 bsdNode->v_parent = fatVolume->pm_bpcluster * dirClust;
744 assign_inode(bsdVolume, &bsdNode->v_parent);
745 }
746 // For a directory other than the root directory, there is no easy way to
747 // ID the parent. That Will be done in later (in dosfs_walk / dosfs_mkdir).
748
749 bsdNode->v_state = VSTATE_CONSTRUCTED;
750
751 status = vcache_set_constructed(bsdVolume, fatNode->de_inode);
752 if (status != B_OK) {
753 free(fatNode);
754 free(bsdNode);
755 return status;
756 }
757
758 #ifdef DEBUG
759 status = vcache_set_node(bsdVolume, fatNode->de_inode, bsdNode);
760 if (status != B_OK)
761 REPORT_ERROR(status);
762 #endif // DEBUG
763
764 *newNode = bsdNode;
765
766 rw_lock_write_unlock(&bsdNode->v_vnlock->haikuRW);
767
768 return B_OK;
769 }
770
771
772 static status_t
dosfs_walk(fs_volume * volume,fs_vnode * dir,const char * name,ino_t * _id)773 dosfs_walk(fs_volume* volume, fs_vnode* dir, const char* name, ino_t* _id)
774 {
775 vnode* bsdDir = reinterpret_cast<vnode*>(dir->private_node);
776 denode* fatDir = reinterpret_cast<denode*>(bsdDir->v_data);
777 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
778 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
779
780 WriteLocker locker(bsdDir->v_vnlock->haikuRW);
781 // msdosfs_lookup_ino will modify de_fndoffset, de_fndcnt
782
783 if (bsdDir->v_type != VDIR)
784 RETURN_ERROR(B_NOT_A_DIRECTORY);
785
786 ComponentName bsdName((strcmp(name, "..") == 0 ? MAKEENTRY | ISDOTDOT : MAKEENTRY), NOCRED,
787 LOOKUP, 0, name);
788
789 daddr_t dirClust;
790 u_long dirOffset;
791 status_t status = B_FROM_POSIX_ERROR(
792 msdosfs_lookup_ino(bsdDir, NULL, bsdName.Data(), &dirClust, &dirOffset));
793 if (status != B_OK) {
794 entry_cache_add_missing(volume->id, fatDir->de_inode, bsdName.Data()->cn_nameptr);
795 RETURN_ERROR(B_ENTRY_NOT_FOUND);
796 }
797 // msdosfs_lookup_ino will return 0 for cluster number if looking up .. in a directory
798 // whose parent is the root directory, even on FAT32 volumes (which reflects the
799 // value that is meant to be stored in the .. direntry, per the FAT spec)
800 if (FAT32(fatVolume) == true && dirClust == MSDOSFSROOT)
801 dirClust = fatVolume->pm_rootdirblk;
802 vnode* bsdResult;
803 status = assign_inode_and_get(bsdVolume, dirClust, dirOffset, &bsdResult);
804 if (status != B_OK)
805 RETURN_ERROR(status);
806 denode* fatResult = reinterpret_cast<denode*>(bsdResult->v_data);
807
808 if (bsdResult->v_type == VDIR) {
809 // dosfs_read_vnode does not set this for directories because it does not know the
810 // parent inode
811 bsdResult->v_parent = fatDir->de_inode;
812 }
813
814 *_id = fatResult->de_inode;
815
816 entry_cache_add(volume->id, fatDir->de_inode, name, fatResult->de_inode);
817
818 return B_OK;
819 }
820
821
822 static status_t
dosfs_release_vnode(fs_volume * volume,fs_vnode * vnode,bool reenter)823 dosfs_release_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter)
824 {
825 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
826 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
827 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
828
829 FUNCTION_START("inode %" B_PRIdINO " @ %p\n", fatNode->de_inode, bsdNode);
830
831 status_t status = B_OK;
832
833 if ((bsdNode->v_vflag & VV_ROOT) == 0) {
834 WriteLocker locker(bsdNode->v_vnlock->haikuRW);
835 // needed only in this block
836
837 status = B_FROM_POSIX_ERROR(deupdat(fatNode, 0));
838 if (status != B_OK)
839 RETURN_ERROR(status);
840
841 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0)
842 _dosfs_fsync(bsdNode);
843 }
844
845 if (bsdNode->v_type == VREG) {
846 status = file_cache_sync(bsdNode->v_cache);
847 file_cache_delete(bsdNode->v_cache);
848 file_map_delete(bsdNode->v_file_map);
849 } else {
850 status = discard_clusters(bsdNode, 0);
851 }
852
853 vcache_set_constructed(bsdVolume, fatNode->de_inode, false);
854
855 free(fatNode);
856
857 rw_lock_destroy(&bsdNode->v_vnlock->haikuRW);
858
859 free(bsdNode);
860
861 RETURN_ERROR(status);
862 }
863
864
865 status_t
dosfs_remove_vnode(fs_volume * volume,fs_vnode * vnode,bool reenter)866 dosfs_remove_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter)
867 {
868 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
869 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
870 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
871 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
872
873 FUNCTION_START("%" B_PRIu64 " @ %p\n", fatNode->de_inode, bsdNode);
874
875 WriteLocker locker(bsdNode->v_vnlock->haikuRW);
876
877 if (MOUNTED_READ_ONLY(fatVolume) != 0)
878 RETURN_ERROR(B_READ_ONLY_DEVICE);
879
880 status_t status = B_OK;
881
882 if (bsdNode->v_type == VREG) {
883 file_cache_delete(bsdNode->v_cache);
884 bsdNode->v_cache = NULL;
885 file_map_delete(bsdNode->v_file_map);
886 bsdNode->v_file_map = NULL;
887 } else {
888 status = discard_clusters(bsdNode, 0);
889 if (status != B_OK)
890 REPORT_ERROR(status);
891 }
892
893 // truncate the file
894 if (fatNode->de_refcnt <= 0 && fatNode->de_StartCluster != root_start_cluster(fatVolume)) {
895 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW);
896 status = B_FROM_POSIX_ERROR(detrunc(fatNode, static_cast<u_long>(0), 0, NOCRED));
897 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW);
898 if (status != B_OK)
899 REPORT_ERROR(status);
900 }
901 if (status == B_OK) {
902 // remove vnode id from the cache
903 if (find_vnid_in_vcache(bsdVolume, fatNode->de_inode) == B_OK)
904 remove_from_vcache(bsdVolume, fatNode->de_inode);
905
906 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0)
907 _dosfs_sync(bsdVolume, false);
908 }
909
910 free(fatNode);
911
912 locker.Detach();
913 rw_lock_destroy(&bsdNode->v_vnlock->haikuRW);
914
915 free(bsdNode);
916
917 RETURN_ERROR(status);
918 }
919
920
921 static bool
dosfs_can_page(fs_volume * vol,fs_vnode * vnode,void * cookie)922 dosfs_can_page(fs_volume* vol, fs_vnode* vnode, void* cookie)
923 {
924 // ToDo: we're obviously not even asked...
925 return false;
926 }
927
928
929 static status_t
dosfs_read_pages(fs_volume * volume,fs_vnode * vnode,void * cookie,off_t pos,const iovec * vecs,size_t count,size_t * _numBytes)930 dosfs_read_pages(fs_volume* volume, fs_vnode* vnode, void* cookie, off_t pos, const iovec* vecs,
931 size_t count, size_t* _numBytes)
932 {
933 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
934 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
935 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
936
937 FUNCTION_START("%p\n", bsdNode);
938
939 if (bsdNode->v_cache == NULL)
940 return B_BAD_VALUE;
941
942 ReadLocker locker(bsdNode->v_vnlock->haikuRW);
943
944 uint32 vecIndex = 0;
945 size_t vecOffset = 0;
946 size_t bytesLeft = *_numBytes;
947 status_t status;
948
949 while (true) {
950 struct file_io_vec fileVecs[8];
951 size_t fileVecCount = 8;
952 bool bufferOverflow;
953 size_t bytes = bytesLeft;
954
955 status
956 = file_map_translate(bsdNode->v_file_map, pos, bytesLeft, fileVecs, &fileVecCount, 0);
957 if (status != B_OK && status != B_BUFFER_OVERFLOW)
958 break;
959
960 bufferOverflow = status == B_BUFFER_OVERFLOW;
961
962 status = read_file_io_vec_pages(fatVolume->pm_dev->si_fd, fileVecs, fileVecCount, vecs,
963 count, &vecIndex, &vecOffset, &bytes);
964 if (status != B_OK || !bufferOverflow)
965 break;
966
967 pos += bytes;
968 bytesLeft -= bytes;
969 }
970
971 RETURN_ERROR(status);
972 }
973
974
975 static status_t
dosfs_write_pages(fs_volume * volume,fs_vnode * vnode,void * cookie,off_t pos,const iovec * vecs,size_t count,size_t * _numBytes)976 dosfs_write_pages(fs_volume* volume, fs_vnode* vnode, void* cookie, off_t pos, const iovec* vecs,
977 size_t count, size_t* _numBytes)
978 {
979 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
980 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
981 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
982
983 uint32 vecIndex = 0;
984 size_t vecOffset = 0;
985 size_t bytesLeft = *_numBytes;
986 status_t status;
987
988 FUNCTION_START("%p\n", bsdNode);
989
990 if (bsdNode->v_cache == NULL)
991 return B_BAD_VALUE;
992
993 ReadLocker locker(bsdNode->v_vnlock->haikuRW);
994
995 if (MOUNTED_READ_ONLY(fatVolume) != 0)
996 return B_READ_ONLY_DEVICE;
997
998 while (true) {
999 struct file_io_vec fileVecs[8];
1000 size_t fileVecCount = 8;
1001 bool bufferOverflow;
1002 size_t bytes = bytesLeft;
1003
1004 status
1005 = file_map_translate(bsdNode->v_file_map, pos, bytesLeft, fileVecs, &fileVecCount, 0);
1006 if (status != B_OK && status != B_BUFFER_OVERFLOW)
1007 break;
1008
1009 bufferOverflow = status == B_BUFFER_OVERFLOW;
1010
1011 status = write_file_io_vec_pages(fatVolume->pm_dev->si_fd, fileVecs, fileVecCount, vecs,
1012 count, &vecIndex, &vecOffset, &bytes);
1013 if (status != B_OK || !bufferOverflow)
1014 break;
1015
1016 pos += bytes;
1017 bytesLeft -= bytes;
1018 }
1019
1020 RETURN_ERROR(status);
1021 }
1022
1023
1024 static status_t
dosfs_io(fs_volume * volume,fs_vnode * vnode,void * cookie,io_request * request)1025 dosfs_io(fs_volume* volume, fs_vnode* vnode, void* cookie, io_request* request)
1026 {
1027 #if KDEBUG_RW_LOCK_DEBUG
1028 // dosfs_io depends on read-locks being implicitly transferrable across threads.
1029 return B_UNSUPPORTED;
1030 #endif
1031 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
1032 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
1033 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
1034 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
1035
1036 #ifndef FS_SHELL
1037 if (io_request_is_write(request) && MOUNTED_READ_ONLY(fatVolume) != 0) {
1038 notify_io_request(request, B_READ_ONLY_DEVICE);
1039 return B_READ_ONLY_DEVICE;
1040 }
1041 #endif
1042
1043 if (bsdNode->v_cache == NULL) {
1044 #ifndef FS_SHELL
1045 notify_io_request(request, B_BAD_VALUE);
1046 #endif
1047 panic("dosfs_io: no file cache\n");
1048 RETURN_ERROR(B_BAD_VALUE);
1049 }
1050
1051 // divert to synchronous IO?
1052 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0 || bsdNode->v_sync == true)
1053 return B_UNSUPPORTED;
1054
1055 rw_lock_read_lock(&bsdNode->v_vnlock->haikuRW);
1056
1057 acquire_vnode(volume, fatNode->de_inode);
1058
1059 RETURN_ERROR(do_iterative_fd_io(fatVolume->pm_dev->si_fd, request, iterative_io_get_vecs_hook,
1060 iterative_io_finished_hook, bsdNode));
1061 }
1062
1063
1064 static status_t
dosfs_get_file_map(fs_volume * volume,fs_vnode * vnode,off_t position,size_t length,struct file_io_vec * vecs,size_t * _count)1065 dosfs_get_file_map(fs_volume* volume, fs_vnode* vnode, off_t position, size_t length,
1066 struct file_io_vec* vecs, size_t* _count)
1067 {
1068 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
1069 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
1070 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
1071 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
1072
1073 FUNCTION_START("%" B_PRIuSIZE " bytes at %" B_PRIdOFF " (vnode id %" B_PRIdINO " at %p)\n",
1074 length, position, fatNode->de_inode, bsdNode);
1075
1076 size_t max = *_count;
1077 *_count = 0;
1078
1079 if ((bsdNode->v_type & VDIR) != 0)
1080 return B_IS_A_DIRECTORY;
1081
1082 if (position < 0)
1083 position = 0;
1084
1085 size_t fileSize = fatNode->de_FileSize;
1086
1087 if (fileSize == 0 || length == 0 || static_cast<u_long>(position) >= fileSize)
1088 return B_OK;
1089
1090 // truncate to file size, taking overflow into account
1091 if (static_cast<uint64>(position + length) >= fileSize
1092 || static_cast<off_t>(position + length) < position) {
1093 length = fileSize - position;
1094 }
1095
1096 csi iter;
1097 status_t status = init_csi(fatVolume, fatNode->de_StartCluster, 0, &iter);
1098 if (status != B_OK)
1099 RETURN_ERROR(B_IO_ERROR);
1100
1101 size_t bytesPerSector = fatVolume->pm_BytesPerSec;
1102
1103 // file-relative sector in which position lies
1104 uint32 positionSector = position / bytesPerSector;
1105
1106 if (positionSector > 0) {
1107 status = iter_csi(&iter, positionSector);
1108 if (status != B_OK)
1109 RETURN_ERROR(status);
1110 }
1111
1112 status = validate_cs(iter.fatVolume, iter.cluster, iter.sector);
1113 if (status != B_OK)
1114 RETURN_ERROR(status);
1115
1116 int32 sectorOffset = position % bytesPerSector;
1117 size_t index = 0;
1118
1119 // Each iteration populates one vec
1120 while (length > 0) {
1121 off_t initFsSector = fs_sector(&iter);
1122 uint32 sectors = 1;
1123
1124 length -= min_c(length, bytesPerSector - sectorOffset);
1125
1126 // Each iteration advances iter to the next sector of the file.
1127 // Break when iter reaches the first sector of a non-contiguous cluster.
1128 while (length > 0) {
1129 status = iter_csi(&iter, 1);
1130 ASSERT(status == B_OK);
1131 status = validate_cs(iter.fatVolume, iter.cluster, iter.sector);
1132 if (status != B_OK)
1133 RETURN_ERROR(status);
1134
1135 if (initFsSector + sectors != fs_sector(&iter)) {
1136 // disjoint sectors, need to flush and begin a new vector
1137 break;
1138 }
1139
1140 length -= min_c(length, bytesPerSector);
1141 sectors++;
1142 }
1143
1144 vecs[index].offset = initFsSector * bytesPerSector + sectorOffset;
1145 vecs[index].length = sectors * bytesPerSector - sectorOffset;
1146 position += vecs[index].length;
1147
1148 // for the last vector only, extend to the end of the last cluster
1149 if (length == 0) {
1150 if (IS_FIXED_ROOT(fatNode) == 0) {
1151 uint32 remainder = position % fatVolume->pm_bpcluster;
1152 if (remainder != 0)
1153 vecs[index].length += (fatVolume->pm_bpcluster - remainder);
1154 }
1155 }
1156
1157 index++;
1158
1159 if (index >= max) {
1160 // we're out of file_io_vecs; let's bail out
1161 *_count = index;
1162 return B_BUFFER_OVERFLOW;
1163 }
1164
1165 sectorOffset = 0;
1166 }
1167
1168 *_count = index;
1169
1170 return B_OK;
1171 }
1172
1173
1174 static status_t
dosfs_fsync(fs_volume * volume,fs_vnode * vnode)1175 dosfs_fsync(fs_volume* volume, fs_vnode* vnode)
1176 {
1177 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
1178
1179 FUNCTION_START("%p\n", bsdNode);
1180
1181 return _dosfs_fsync(bsdNode);
1182 }
1183
1184
1185 static status_t
_dosfs_fsync(struct vnode * bsdNode)1186 _dosfs_fsync(struct vnode* bsdNode)
1187 {
1188 mount* bsdVolume = bsdNode->v_mount;
1189 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
1190 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
1191
1192 ReadLocker locker(bsdNode->v_vnlock->haikuRW);
1193
1194 status_t status = B_OK;
1195 if (bsdNode->v_cache != NULL) {
1196 PRINT("fsync: file_cache_sync\n");
1197 status = file_cache_sync(bsdNode->v_cache);
1198 } else {
1199 status = sync_clusters(bsdNode);
1200 }
1201
1202 // If user chose op sync mode, flush the whole block cache. This will ensure that
1203 // the metadata that is external to the direntry (FAT chain for this file and all directory
1204 // files in the hierarchy above this file) is also synced. If not, just sync the FAT and the
1205 // node's direntry, if it has one (the root directory doesn't).
1206 status_t externStatus = B_OK;
1207
1208 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0) {
1209 externStatus = block_cache_sync(bsdVolume->mnt_cache);
1210 if (externStatus != B_OK)
1211 REPORT_ERROR(externStatus);
1212 } else {
1213 size_t fatBlocks = (fatVolume->pm_fatsize * fatVolume->pm_FATs) / DEV_BSIZE;
1214 status_t fatStatus
1215 = block_cache_sync_etc(bsdVolume->mnt_cache, fatVolume->pm_fatblk, fatBlocks);
1216 if (fatStatus != B_OK) {
1217 externStatus = fatStatus;
1218 REPORT_ERROR(fatStatus);
1219 }
1220 if ((bsdNode->v_vflag & VV_ROOT) == 0) {
1221 status_t entryStatus = B_FROM_POSIX_ERROR(deupdat(fatNode, 1));
1222 if (entryStatus != B_OK) {
1223 externStatus = entryStatus;
1224 REPORT_ERROR(entryStatus);
1225 }
1226 }
1227 }
1228
1229 if (status == B_OK)
1230 status = externStatus;
1231
1232 RETURN_ERROR(status);
1233 }
1234
1235
1236 static status_t
dosfs_link(fs_volume * volume,fs_vnode * dir,const char * name,fs_vnode * vnode)1237 dosfs_link(fs_volume* volume, fs_vnode* dir, const char* name, fs_vnode* vnode)
1238 {
1239 FUNCTION_START("attempt to assign %s to %p in directory %p\n", name, vnode, dir);
1240
1241 return B_UNSUPPORTED;
1242 }
1243
1244
1245 static status_t
dosfs_unlink(fs_volume * volume,fs_vnode * dir,const char * name)1246 dosfs_unlink(fs_volume* volume, fs_vnode* dir, const char* name)
1247 {
1248 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
1249 vnode* bsdDir = reinterpret_cast<vnode*>(dir->private_node);
1250 denode* fatDir = reinterpret_cast<denode*>(bsdDir->v_data);
1251 vnode* bsdNode = NULL;
1252 denode* fatNode = NULL;
1253
1254 FUNCTION_START("%s in directory @ %p\n", name, bsdDir);
1255
1256 if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
1257 return B_NOT_ALLOWED;
1258
1259 ComponentName bsdName(ISLASTCN, NOCRED, DELETE, 0, name);
1260
1261 // multiple unlinks of files in the same dir would interfere when msdosfs_lookup_ino sets
1262 // de_fndofset and de_fndcnt of the parent node
1263 WriteLocker dirLocker(bsdDir->v_vnlock->haikuRW);
1264
1265 // set bsdNode to the file to be removed
1266 daddr_t cluster;
1267 u_long offset;
1268 status_t status
1269 = B_FROM_POSIX_ERROR(msdosfs_lookup_ino(bsdDir, NULL, bsdName.Data(), &cluster, &offset));
1270 if (status != B_OK)
1271 RETURN_ERROR(status);
1272 status = assign_inode_and_get(bsdVolume, cluster, offset, &bsdNode);
1273 if (status != B_OK)
1274 RETURN_ERROR(status);
1275 WriteLocker nodeLocker(bsdNode->v_vnlock->haikuRW);
1276 NodePutter nodePutter(bsdNode);
1277 fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
1278
1279 if (bsdNode->v_type == VDIR)
1280 return B_IS_A_DIRECTORY;
1281
1282 status = _dosfs_access(bsdVolume, bsdNode, W_OK);
1283 if (status != B_OK)
1284 RETURN_ERROR(B_NOT_ALLOWED);
1285
1286 status = B_FROM_POSIX_ERROR(removede(fatDir, fatNode));
1287 if (status != B_OK)
1288 RETURN_ERROR(status);
1289
1290 // Set the loc to a unique value. This effectively removes it from the
1291 // vcache without releasing its vnid for reuse. It also nicely reserves
1292 // the vnid from use by other nodes. This is okay because the vnode is
1293 // locked in memory after this point and loc will not be referenced from
1294 // here on.
1295 ino_t ino = fatNode->de_inode;
1296 status = vcache_set_entry(bsdVolume, ino, generate_unique_vnid(bsdVolume));
1297 if (status != B_OK)
1298 RETURN_ERROR(status);
1299
1300 status = remove_vnode(volume, ino);
1301 if (status != B_OK)
1302 RETURN_ERROR(status);
1303
1304 status = entry_cache_remove(volume->id, fatDir->de_inode, name);
1305 if (status != B_OK)
1306 REPORT_ERROR(status);
1307
1308 notify_entry_removed(volume->id, fatDir->de_inode, name, ino);
1309
1310 nodeLocker.Unlock();
1311
1312 if (status == B_OK && (bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0) {
1313 // sync the parent directory changes
1314 _dosfs_sync(bsdVolume, false);
1315 }
1316
1317 RETURN_ERROR(status);
1318 }
1319
1320
1321 /*!
1322 What follows is the basic algorithm:
1323
1324 if (file move) {
1325 if (dest file exists)
1326 remove dest file
1327 if (dest and src in same directory) {
1328 rewrite name in existing directory slot
1329 } else {
1330 write new entry in dest directory
1331 update offset and dirclust in denode
1332 clear old directory entry
1333 }
1334 } else {
1335 directory move
1336 if (dest directory exists) {
1337 if (dest is not empty)
1338 return ENOTEMPTY
1339 remove dest directory
1340 }
1341 if (dest and src in same directory)
1342 rewrite name in existing entry
1343 else {
1344 be sure dest is not a child of src directory
1345 write entry in dest directory
1346 update "." and ".." in moved directory
1347 clear old directory entry for moved directory
1348 }
1349 }
1350 */
1351 status_t
dosfs_rename(fs_volume * volume,fs_vnode * fromDir,const char * fromName,fs_vnode * toDir,const char * toName)1352 dosfs_rename(fs_volume* volume, fs_vnode* fromDir, const char* fromName, fs_vnode* toDir,
1353 const char* toName)
1354 {
1355 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
1356 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
1357 vnode* fromDirBsdNode = reinterpret_cast<vnode*>(fromDir->private_node);
1358 vnode* toDirBsdNode = reinterpret_cast<vnode*>(toDir->private_node);
1359
1360 if (fromDir == toDir && !strcmp(fromName, toName))
1361 return B_OK;
1362
1363 if (is_filename_legal(toName) == false) {
1364 INFORM("file name '%s' is not permitted in the FAT filesystem\n", toName);
1365 return B_BAD_VALUE;
1366 }
1367
1368 ComponentName fromBsdName(ISLASTCN, NOCRED, RENAME, 0, fromName);
1369 ComponentName toBsdName(ISLASTCN, NOCRED, RENAME, 0, toName);
1370
1371 // Don't do 2 renames at the same time on the same volume. If moving to a new directory,
1372 // and the destination directory of one thread is the origin directory of the other,
1373 // and vice versa, a deadlock can occur.
1374 MutexLocker volumeLocker(bsdVolume->mnt_mtx.haikuMutex);
1375
1376 WriteLocker fromDirLocker(fromDirBsdNode->v_vnlock->haikuRW);
1377 WriteLocker toDirLocker;
1378 if (fromDirBsdNode != toDirBsdNode)
1379 toDirLocker.SetTo(toDirBsdNode->v_vnlock->haikuRW, false);
1380
1381 status_t status = _dosfs_access(bsdVolume, fromDirBsdNode, W_OK);
1382 if (status == B_OK && fromDirBsdNode != toDirBsdNode)
1383 status = _dosfs_access(bsdVolume, toDirBsdNode, W_OK);
1384 if (status != B_OK)
1385 RETURN_ERROR(status);
1386
1387 // get the 'from' node
1388 daddr_t fromCluster;
1389 u_long fromOffset;
1390 status = B_FROM_POSIX_ERROR(
1391 msdosfs_lookup_ino(fromDirBsdNode, NULL, fromBsdName.Data(), &fromCluster, &fromOffset));
1392 if (status != B_OK)
1393 RETURN_ERROR(status);
1394 vnode* fromBsdNode;
1395 status = assign_inode_and_get(bsdVolume, fromCluster, fromOffset, &fromBsdNode);
1396 if (status != B_OK)
1397 RETURN_ERROR(status);
1398 NodePutter fromPutter(fromBsdNode);
1399 WriteLocker fromLocker(fromBsdNode->v_vnlock->haikuRW);
1400
1401 // make sure the from entry wasn't deleted before we locked it
1402 status = B_FROM_POSIX_ERROR(
1403 msdosfs_lookup_ino(fromDirBsdNode, NULL, fromBsdName.Data(), &fromCluster, &fromOffset));
1404 if (status != B_OK) {
1405 INFORM("dosfs_rename: file no longer present\n");
1406 RETURN_ERROR(status);
1407 }
1408
1409 // get the "to" node, if the target name already exists
1410 daddr_t toCluster;
1411 u_long toOffset;
1412 status = B_FROM_POSIX_ERROR(
1413 msdosfs_lookup_ino(toDirBsdNode, NULL, toBsdName.Data(), &toCluster, &toOffset));
1414 if (status != B_OK && status != B_FROM_POSIX_ERROR(EJUSTRETURN))
1415 RETURN_ERROR(status);
1416 vnode* toBsdNode = NULL;
1417 if (status == B_OK) {
1418 // the target name does exist
1419 status = assign_inode_and_get(bsdVolume, toCluster, toOffset, &toBsdNode);
1420 if (status != B_OK)
1421 RETURN_ERROR(status);
1422 }
1423
1424 // Is toName equivalent to fromName in the FAT filesystem?
1425 bool caseChange = false;
1426 if (fromBsdNode == toBsdNode) {
1427 // The names they must differ only in capitalization. Ignore the match that was found for
1428 // the "to" node.
1429 put_vnode(volume, reinterpret_cast<denode*>(toBsdNode->v_data)->de_inode);
1430 toBsdNode = NULL;
1431 caseChange = true;
1432 }
1433
1434 NodePutter toPutter;
1435 WriteLocker toLocker;
1436
1437 if (toBsdNode != NULL) {
1438 status = msdosfs_lookup_ino(toDirBsdNode, NULL, toBsdName.Data(), &toCluster, &toOffset);
1439 if (status != 0) {
1440 toBsdNode = NULL;
1441 status = B_OK;
1442 } else {
1443 toLocker.SetTo(toBsdNode->v_vnlock->haikuRW, false);
1444 toPutter.SetTo(toBsdNode);
1445 }
1446 }
1447
1448 denode* fromDirFatNode = reinterpret_cast<denode*>(fromDirBsdNode->v_data);
1449 denode* fromFatNode = reinterpret_cast<denode*>(fromBsdNode->v_data);
1450 denode* toDirFatNode = reinterpret_cast<denode*>(toDirBsdNode->v_data);
1451 denode* toFatNode = toBsdNode != NULL ? reinterpret_cast<denode*>(toBsdNode->v_data) : NULL;
1452
1453 PRINT("dosfs_rename: %" B_PRIu64 "/%s->%" B_PRIu64 "/%s\n", fromDirFatNode->de_inode, fromName,
1454 toDirFatNode->de_inode, toName);
1455
1456 u_long toDirOffset = toDirFatNode->de_fndoffset;
1457
1458 // Is fromName a directory?
1459 bool doingDirectory = false;
1460 // Be sure we are not renaming ".", "..", or an alias of ".". This leads to a
1461 // crippled directory tree. It's pretty tough to do a "ls" or "pwd" with the
1462 // "." directory entry missing, and "cd .."doesn't work if the ".." entry is missing.
1463 if ((fromFatNode->de_Attributes & ATTR_DIRECTORY) != 0) {
1464 // Avoid ".", "..", and aliases of "." for obvious reasons.
1465 if ((fromBsdName.Data()->cn_namelen == 1 && fromBsdName.Data()->cn_nameptr[0] == '.')
1466 || fromDirFatNode == fromFatNode || (fromBsdName.Data()->cn_flags & ISDOTDOT) != 0
1467 || (fromBsdName.Data()->cn_flags & ISDOTDOT) != 0) {
1468 RETURN_ERROR(B_BAD_VALUE);
1469 }
1470 doingDirectory = true;
1471 }
1472
1473 // Is the target being moved to new parent directory?
1474 bool newParent = fromDirFatNode != toDirFatNode ? true : false;
1475
1476 // If ".." must be changed (ie the directory gets a new parent) then the source
1477 // directory must not be in the directory hierarchy above the target, as this would
1478 // orphan everything below the source directory. Also the user must have write
1479 // permission in the source so as to be able to change "..".
1480 status = _dosfs_access(bsdVolume, fromBsdNode, W_OK);
1481 if (doingDirectory && newParent) {
1482 if (status != B_OK) // write access check above
1483 RETURN_ERROR(status);
1484
1485 rw_lock_write_lock(&fatVolume->pm_checkpath_lock.haikuRW);
1486
1487 // The BSD function doscheckpath requires a third argument to return the location of
1488 // any child directory of fromFatNode that is locked by another thread. In the port we
1489 // don't use make use of this information, we just wait for that node to be unlocked.
1490 daddr_t dummy;
1491 // Switch the 'to' directory from the WriteLocker to a simple lock. This is a workaround
1492 // for problems that occur when doscheckpath() works with the node lock, while that lock
1493 // is held by a WriteLocker.
1494 rw_lock_write_lock(&toDirBsdNode->v_vnlock->haikuRW);
1495 toDirLocker.Unlock();
1496 status = B_FROM_POSIX_ERROR(doscheckpath(fromFatNode, toDirFatNode, &dummy));
1497 toDirLocker.Lock();
1498 rw_lock_write_unlock(&toDirBsdNode->v_vnlock->haikuRW);
1499
1500 rw_lock_write_unlock(&fatVolume->pm_checkpath_lock.haikuRW);
1501 if (status != B_OK)
1502 RETURN_ERROR(status);
1503 }
1504
1505 if (toFatNode != NULL) {
1506 // Target must be empty if a directory and have no links to it. Also, ensure source and
1507 // target are compatible (both directories, or both not directories).
1508 if ((toFatNode->de_Attributes & ATTR_DIRECTORY) != 0) {
1509 if (!dosdirempty(toFatNode))
1510 RETURN_ERROR(B_DIRECTORY_NOT_EMPTY);
1511 if (!doingDirectory)
1512 RETURN_ERROR(B_NOT_A_DIRECTORY);
1513 entry_cache_remove(volume->id, toDirFatNode->de_inode, toBsdName.Data()->cn_nameptr);
1514 } else if (doingDirectory) {
1515 RETURN_ERROR(B_IS_A_DIRECTORY);
1516 }
1517
1518 // delete the file/directory that we are overwriting
1519 daddr_t remCluster;
1520 u_long remOffset;
1521 status = msdosfs_lookup_ino(toDirBsdNode, NULL, toBsdName.Data(), &remCluster, &remOffset);
1522 // set de_fndoffset for use by removede
1523 status = B_FROM_POSIX_ERROR(removede(toDirFatNode, toFatNode));
1524 if (status != B_OK)
1525 RETURN_ERROR(status);
1526
1527 // Set the loc to a unique value. This effectively removes it from the vcache without
1528 // releasing its vnid for reuse. It also nicely reserves the vnid from use by other
1529 // nodes. This is okay because the vnode is locked in memory after this point and loc
1530 // will not be referenced from here on.
1531 vcache_set_entry(bsdVolume, toFatNode->de_inode, generate_unique_vnid(bsdVolume));
1532
1533 entry_cache_remove(volume->id, toDirFatNode->de_inode, toName);
1534 notify_entry_removed(volume->id, toDirFatNode->de_inode, toName, toFatNode->de_inode);
1535
1536 remove_vnode(volume, toFatNode->de_inode);
1537
1538 toLocker.Unlock();
1539 toPutter.Put();
1540
1541 toBsdNode = NULL;
1542 toFatNode = NULL;
1543 }
1544
1545 // Convert the filename in toBsdName into a dos filename. We copy this into the denode and
1546 // directory entry for the destination file/directory.
1547 u_char toShortName[SHORTNAME_CSTRING], oldShortNameArray[SHORTNAME_LENGTH];
1548 if (caseChange == false) {
1549 status = B_FROM_POSIX_ERROR(uniqdosname(toDirFatNode, toBsdName.Data(), toShortName));
1550 if (status != B_OK)
1551 RETURN_ERROR(status);
1552 if (is_shortname_legal(toShortName) == false)
1553 return B_NOT_ALLOWED;
1554 }
1555 // if only changing case, the dos filename (always all-caps) will remain the same
1556
1557 // First write a new entry in the destination directory and mark the entry in the source
1558 // directory as deleted. If we moved a directory, then update its .. entry to point to
1559 // the new parent directory.
1560 if (caseChange == false) {
1561 memcpy(oldShortNameArray, fromFatNode->de_Name, SHORTNAME_LENGTH);
1562 memcpy(fromFatNode->de_Name, toShortName, SHORTNAME_LENGTH); // update denode
1563 } else {
1564 // We prefer to create the new dir entry before removing the old one, but if only
1565 // changing case, we remove the old dir entry first, so that msdosfs_lookup_ino call below
1566 // won't see it as a match for the to-name when it does its case-insensitive search,
1567 // which would cause it to return before it has found empty slots for the new dir entry.
1568 status = B_FROM_POSIX_ERROR(removede(fromDirFatNode, fromFatNode));
1569 if (status != B_OK) {
1570 INFORM("rename removede error: %" B_PRIu64 "/%" B_PRIu64 ": %s\n",
1571 fromDirFatNode->de_inode, fromFatNode->de_inode, strerror(status));
1572 msdosfs_integrity_error(fatVolume);
1573 RETURN_ERROR(status);
1574 }
1575 }
1576
1577 daddr_t createCluster;
1578 u_long createOffset;
1579 status
1580 = msdosfs_lookup_ino(toDirBsdNode, NULL, toBsdName.Data(), &createCluster, &createOffset);
1581 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW);
1582 // the FAT will be updated if the directory needs to be extended to hold another dirent
1583 if (status == EJUSTRETURN) {
1584 toDirFatNode->de_fndoffset = toDirOffset;
1585 // if the to-name already existed, ensure that creatde will write the new
1586 // direntry to the space previously occupied by the (removed) to-name entry
1587 status = createde(fromFatNode, toDirFatNode, NULL, toBsdName.Data());
1588 }
1589 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW);
1590 if (status != B_OK) {
1591 if (caseChange == true) {
1592 // We failed to create the new dir entry, and the old dir entry is already gone.
1593 // Try to restore the old entry. Since the old name is a case variant of the new name
1594 // in the same directory, creating an entry with the old name will probably fail too.
1595 // Use the dos name instead of the long name, to simplify entry creation and try to
1596 // avoid the same mode of failure.
1597 ComponentName restoreName(ISLASTCN, NOCRED, CREATE, 0,
1598 reinterpret_cast<char*>(fromFatNode->de_Name));
1599 createde(fromFatNode, fromDirFatNode, NULL, restoreName.Data());
1600 } else {
1601 // we haven't removed the old dir entry yet
1602 memcpy(fromFatNode->de_Name, oldShortNameArray, SHORTNAME_LENGTH);
1603 }
1604 RETURN_ERROR(B_FROM_POSIX_ERROR(status));
1605 }
1606
1607 // If fromFatNode is for a directory, then its name should always be "." since it is for the
1608 // directory entry in the directory itself (msdosfs_lookup() always translates to the "."
1609 // entry so as to get a unique denode, except for the root directory there are different
1610 // complications). However, we just corrupted its name to pass the correct name to
1611 // createde(). Undo this.
1612 if ((fromFatNode->de_Attributes & ATTR_DIRECTORY) != 0)
1613 memcpy(fromFatNode->de_Name, oldShortNameArray, SHORTNAME_LENGTH);
1614 fromFatNode->de_refcnt++;
1615 // offset the decrement that will occur in removede
1616 daddr_t remFromCluster;
1617 u_long remFromOffset;
1618 status = msdosfs_lookup_ino(fromDirBsdNode, NULL, fromBsdName.Data(), &remFromCluster,
1619 &remFromOffset);
1620 if (caseChange == false) {
1621 status = B_FROM_POSIX_ERROR(removede(fromDirFatNode, fromFatNode));
1622 if (status != B_OK) {
1623 INFORM("rename removede error: %" B_PRIu64 "/%" B_PRIu64 ": %s\n",
1624 fromDirFatNode->de_inode, fromFatNode->de_inode, strerror(status));
1625 msdosfs_integrity_error(fatVolume);
1626 RETURN_ERROR(status);
1627 }
1628 }
1629 if (!doingDirectory) {
1630 status = B_FROM_POSIX_ERROR(pcbmap(toDirFatNode, de_cluster(fatVolume, toDirOffset), 0,
1631 &fromFatNode->de_dirclust, 0));
1632 if (status != B_OK) {
1633 msdosfs_integrity_error(fatVolume);
1634 // fs is corrupt
1635 RETURN_ERROR(status);
1636 }
1637 if (fromFatNode->de_dirclust == MSDOSFSROOT)
1638 fromFatNode->de_diroffset = toDirOffset;
1639 else
1640 fromFatNode->de_diroffset = toDirOffset & fatVolume->pm_crbomask;
1641 }
1642
1643 fromBsdNode->v_parent = toDirFatNode->de_inode;
1644
1645 ino_t newLocation = DETOI(fatVolume, fromFatNode->de_dirclust, fromFatNode->de_diroffset);
1646 vcache_set_entry(bsdVolume, fromFatNode->de_inode, newLocation);
1647
1648 // If we moved a directory to a new parent directory, then we must fixup the ".." entry in
1649 // the moved directory.
1650 if (doingDirectory && newParent) {
1651 buf* dotDotBuf = NULL;
1652 u_long clustNumber = fromFatNode->de_StartCluster;
1653 ASSERT(clustNumber != MSDOSFSROOT);
1654 // this should never happen
1655 daddr_t blockNumber = cntobn(fatVolume, clustNumber);
1656 status = B_FROM_POSIX_ERROR(
1657 bread(fatVolume->pm_devvp, blockNumber, fatVolume->pm_bpcluster, NOCRED, &dotDotBuf));
1658 if (status != B_OK) {
1659 INFORM("rename read error: %" B_PRIu64 "/%" B_PRIu64 ": %s\n",
1660 fromDirFatNode->de_inode, fromFatNode->de_inode, strerror(status));
1661 msdosfs_integrity_error(fatVolume);
1662 RETURN_ERROR(status);
1663 }
1664 direntry* dotDotEntry = reinterpret_cast<direntry*>(dotDotBuf->b_data) + 1;
1665 u_long parentClust = toDirFatNode->de_StartCluster;
1666 if (FAT32(fatVolume) == true && parentClust == fatVolume->pm_rootdirblk)
1667 parentClust = MSDOSFSROOT;
1668 putushort(dotDotEntry->deStartCluster, parentClust);
1669 if (FAT32(fatVolume) == true)
1670 putushort(dotDotEntry->deHighClust, parentClust >> 16);
1671 if (DOINGASYNC(fromBsdNode)) {
1672 bdwrite(dotDotBuf);
1673 } else if ((status = B_FROM_POSIX_ERROR(bwrite(dotDotBuf))) != B_OK) {
1674 INFORM("rename write error: %" B_PRIu64 "/%" B_PRIu64 ": %s\n",
1675 fromDirFatNode->de_inode, fromFatNode->de_inode, strerror(status));
1676 msdosfs_integrity_error(fatVolume);
1677 RETURN_ERROR(status);
1678 }
1679 entry_cache_add(volume->id, fromFatNode->de_inode, "..", toDirFatNode->de_inode);
1680 }
1681
1682 status = entry_cache_remove(volume->id, fromDirFatNode->de_inode, fromName);
1683 if (status != B_OK)
1684 REPORT_ERROR(status);
1685 status = entry_cache_add(volume->id, toDirFatNode->de_inode, toName, fromFatNode->de_inode);
1686 if (status != B_OK)
1687 REPORT_ERROR(status);
1688
1689 status = notify_entry_moved(volume->id, fromDirFatNode->de_inode, fromName,
1690 toDirFatNode->de_inode, toName, fromFatNode->de_inode);
1691 if (status != B_OK)
1692 REPORT_ERROR(status);
1693
1694 set_mime_type(fromBsdNode, true);
1695
1696 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0) {
1697 // sync the directory entry changes
1698 status = block_cache_sync(bsdVolume->mnt_cache);
1699 }
1700
1701 RETURN_ERROR(status);
1702 }
1703
1704
1705 static status_t
dosfs_access(fs_volume * vol,fs_vnode * node,int mode)1706 dosfs_access(fs_volume* vol, fs_vnode* node, int mode)
1707 {
1708 mount* bsdVolume = reinterpret_cast<mount*>(vol->private_volume);
1709 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(node->private_node);
1710
1711 ReadLocker locker(bsdNode->v_vnlock->haikuRW);
1712
1713 RETURN_ERROR(_dosfs_access(bsdVolume, bsdNode, mode));
1714 }
1715
1716
1717 status_t
_dosfs_access(const mount * bsdVolume,const struct vnode * bsdNode,const int mode)1718 _dosfs_access(const mount* bsdVolume, const struct vnode* bsdNode, const int mode)
1719 {
1720 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
1721
1722 if ((mode & W_OK) != 0 && MOUNTED_READ_ONLY(fatVolume))
1723 RETURN_ERROR(B_READ_ONLY_DEVICE);
1724
1725 mode_t fileMode = 0;
1726 mode_bits(bsdNode, &fileMode);
1727
1728 // userlandfs does not provide check_access_permissions
1729 #ifdef USER
1730 return check_access_permissions_internal(mode, fileMode, fatVolume->pm_gid, fatVolume->pm_uid);
1731 #else
1732 return check_access_permissions(mode, fileMode, fatVolume->pm_gid, fatVolume->pm_uid);
1733 #endif
1734 }
1735
1736
1737 static status_t
dosfs_rstat(fs_volume * volume,fs_vnode * vnode,struct stat * stat)1738 dosfs_rstat(fs_volume* volume, fs_vnode* vnode, struct stat* stat)
1739 {
1740 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
1741 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
1742 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
1743 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
1744
1745 ReadLocker locker(bsdNode->v_vnlock->haikuRW);
1746
1747 // file mode bits
1748 mode_bits(bsdNode, &stat->st_mode);
1749 // file type bits
1750 status_t status = B_OK;
1751 if (bsdNode->v_type == VDIR)
1752 stat->st_mode |= S_IFDIR;
1753 else if (bsdNode->v_type == VREG)
1754 stat->st_mode |= S_IFREG;
1755 else
1756 status = B_BAD_VALUE;
1757
1758 stat->st_nlink = 1;
1759
1760 // The FAT filesystem does not keep track of ownership at the file level
1761 stat->st_uid = fatVolume->pm_uid;
1762
1763 stat->st_gid = fatVolume->pm_gid;
1764
1765 stat->st_size = fatNode->de_FileSize;
1766
1767 stat->st_blksize = FAT_IO_SIZE;
1768
1769 fattime2timespec(fatNode->de_MDate, fatNode->de_MTime, 0, 1, &stat->st_mtim);
1770
1771 // FAT does not keep a record of last change time
1772 stat->st_ctim = stat->st_mtim;
1773
1774 fattime2timespec(fatNode->de_ADate, 0, 0, 1, &stat->st_atim);
1775
1776 fattime2timespec(fatNode->de_CDate, fatNode->de_CTime, fatNode->de_CHun, 1, &stat->st_crtim);
1777
1778 stat->st_blocks = howmany(fatNode->de_FileSize, 512);
1779
1780 RETURN_ERROR(status);
1781 }
1782
1783
1784 static status_t
dosfs_wstat(fs_volume * volume,fs_vnode * vnode,const struct stat * stat,uint32 statMask)1785 dosfs_wstat(fs_volume* volume, fs_vnode* vnode, const struct stat* stat, uint32 statMask)
1786 {
1787 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
1788 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
1789 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
1790 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
1791
1792 FUNCTION_START("inode %" B_PRIu64 ", @ %p\n", fatNode->de_inode, bsdNode);
1793
1794 WriteLocker locker(bsdNode->v_vnlock->haikuRW);
1795
1796 bool hasWriteAccess = _dosfs_access(bsdVolume, bsdNode, W_OK) == B_OK;
1797 uid_t uid = geteuid();
1798 bool isOwnerOrRoot = uid == 0 || uid == fatVolume->pm_uid;
1799 ;
1800
1801 // We don't allow setting attributes on the root directory. The special case for the root
1802 // directory is because before FAT32, the root directory didn't have an entry for itself
1803 // (and was otherwise special). With FAT32, the root directory is not so special, but still
1804 // doesn't have an entry for itself.
1805 if (bsdNode->v_vflag & VV_ROOT)
1806 RETURN_ERROR(B_BAD_VALUE);
1807
1808 off_t previousSize = fatNode->de_FileSize;
1809 status_t status = B_OK;
1810
1811 if ((statMask & B_STAT_SIZE) != 0) {
1812 if (!hasWriteAccess)
1813 RETURN_ERROR(B_NOT_ALLOWED);
1814
1815 switch (bsdNode->v_type) {
1816 case VDIR:
1817 return B_IS_A_DIRECTORY;
1818 case VREG:
1819 break;
1820 default:
1821 return B_BAD_VALUE;
1822 break;
1823 }
1824
1825 if (stat->st_size >= MSDOSFS_FILESIZE_MAX)
1826 RETURN_ERROR(B_FILE_TOO_LARGE);
1827
1828 bool shrinking = previousSize > stat->st_size;
1829
1830 // If growing the file, detrunc will call deextend, which tries to zero out the new
1831 // clusters. We use the v_resizing flag to disable writes during detrunc to prevent that,
1832 // because using file_cache_write while the node is locked can cause a deadlock.
1833 // The new clusters will be cleared after return from detrunc instead.
1834 // We also disable writes in the case of shrinking the file because, unlike the detrunc
1835 // call in create or open, which always truncate to zero, this call will most likely pass
1836 // a size that is not a multiple of cluster size, so detrunc will want to zero out the end
1837 // of the last cluster.
1838 bsdNode->v_resizing = true;
1839 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW);
1840 status = B_FROM_POSIX_ERROR(detrunc(fatNode, stat->st_size, 0, NOCRED));
1841 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW);
1842 bsdNode->v_resizing = false;
1843 if (status != B_OK)
1844 RETURN_ERROR(status);
1845
1846 PRINT("dosfs_wstat: inode %" B_PRIu64 ", @ %p size change from %" B_PRIdOFF " to %" B_PRIu64
1847 "\n", fatNode->de_inode, bsdNode, previousSize, stat->st_size);
1848
1849 locker.Unlock();
1850 // avoid deadlock with dosfs_io
1851 file_cache_set_size(bsdNode->v_cache, fatNode->de_FileSize);
1852 if (shrinking == false && (statMask & B_STAT_SIZE_INSECURE) == 0) {
1853 status = fill_gap_with_zeros(bsdNode, previousSize, fatNode->de_FileSize);
1854 if (status != B_OK)
1855 RETURN_ERROR(status);
1856 }
1857 locker.Lock();
1858
1859 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0)
1860 _dosfs_fsync(bsdNode);
1861
1862 fatNode->de_Attributes |= ATTR_ARCHIVE;
1863 fatNode->de_flag |= DE_MODIFIED;
1864 }
1865
1866 // DOS files only have the ability to have their writability attribute set, so we use the
1867 // owner write bit to set the readonly attribute.
1868 if ((statMask & B_STAT_MODE) != 0) {
1869 if (!isOwnerOrRoot)
1870 RETURN_ERROR(B_NOT_ALLOWED);
1871 PRINT("setting file mode to %o\n", stat->st_mode);
1872 if (bsdNode->v_type != VDIR) {
1873 if ((stat->st_mode & S_IWUSR) == 0)
1874 fatNode->de_Attributes |= ATTR_READONLY;
1875 else
1876 fatNode->de_Attributes &= ~ATTR_READONLY;
1877
1878 // We don't set the archive bit when modifying the time of
1879 // a directory to emulate the Windows/DOS behavior.
1880 fatNode->de_Attributes |= ATTR_ARCHIVE;
1881 fatNode->de_flag |= DE_MODIFIED;
1882 }
1883 }
1884
1885 if ((statMask & B_STAT_UID) != 0) {
1886 PRINT("cannot set UID at file level\n");
1887 if (stat->st_uid != fatVolume->pm_uid)
1888 status = B_BAD_VALUE;
1889 }
1890
1891 if ((statMask & B_STAT_GID) != 0) {
1892 PRINT("cannot set GID at file level\n");
1893 if (stat->st_gid != fatVolume->pm_gid)
1894 status = B_BAD_VALUE;
1895 }
1896
1897 if ((statMask & B_STAT_ACCESS_TIME) != 0) {
1898 PRINT("setting access time\n");
1899 fatNode->de_flag &= ~DE_ACCESS;
1900 struct timespec atimGMT;
1901 local_to_GMT(&stat->st_atim, &atimGMT);
1902 timespec2fattime(&atimGMT, 0, &fatNode->de_ADate, NULL, NULL);
1903 if (bsdNode->v_type != VDIR)
1904 fatNode->de_Attributes |= ATTR_ARCHIVE;
1905 fatNode->de_flag |= DE_MODIFIED;
1906 }
1907
1908 if ((statMask & B_STAT_MODIFICATION_TIME) != 0) {
1909 // the user or root can do that or any user with write access
1910 if (!isOwnerOrRoot && !hasWriteAccess)
1911 RETURN_ERROR(B_NOT_ALLOWED);
1912 PRINT("setting modification time\n");
1913 fatNode->de_flag &= ~DE_UPDATE;
1914 struct timespec mtimGMT;
1915 local_to_GMT(&stat->st_mtim, &mtimGMT);
1916 timespec2fattime(&mtimGMT, 0, &fatNode->de_MDate, &fatNode->de_MTime, NULL);
1917 if (bsdNode->v_type != VDIR)
1918 fatNode->de_Attributes |= ATTR_ARCHIVE;
1919 fatNode->de_flag |= DE_MODIFIED;
1920 }
1921
1922 if ((statMask & B_STAT_CREATION_TIME) != 0) {
1923 // the user or root can do that or any user with write access
1924 if (!isOwnerOrRoot && !hasWriteAccess)
1925 RETURN_ERROR(B_NOT_ALLOWED);
1926 PRINT("setting creation time\n");
1927 struct timespec crtimGMT;
1928 local_to_GMT(&stat->st_crtim, &crtimGMT);
1929 timespec2fattime(&crtimGMT, 0, &fatNode->de_CDate, &fatNode->de_CTime, NULL);
1930 fatNode->de_flag |= DE_MODIFIED;
1931 }
1932
1933 // node change time is not recorded in the FAT file system
1934
1935 status = B_FROM_POSIX_ERROR(deupdat(fatNode, (bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0));
1936
1937 notify_stat_changed(volume->id, bsdNode->v_parent, fatNode->de_inode, statMask);
1938
1939 RETURN_ERROR(status);
1940 }
1941
1942
1943 static status_t
dosfs_create(fs_volume * volume,fs_vnode * dir,const char * name,int openMode,int perms,void ** _cookie,ino_t * _newVnodeID)1944 dosfs_create(fs_volume* volume, fs_vnode* dir, const char* name, int openMode, int perms,
1945 void** _cookie, ino_t* _newVnodeID)
1946 {
1947 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
1948 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
1949 vnode* bsdDir = reinterpret_cast<vnode*>(dir->private_node);
1950 denode* fatDir = reinterpret_cast<denode*>(bsdDir->v_data);
1951
1952 FUNCTION_START("create %s in %" B_PRIu64 ", perms = %o openMode =%o\n", name, fatDir->de_inode,
1953 perms, openMode);
1954
1955 ComponentName bsdName(ISLASTCN | MAKEENTRY, NOCRED, CREATE, 0, name);
1956
1957 WriteLocker locker(bsdDir->v_vnlock->haikuRW);
1958
1959 if (_dosfs_access(bsdVolume, bsdDir, open_mode_to_access(openMode)) != B_OK)
1960 RETURN_ERROR(B_NOT_ALLOWED);
1961
1962 if ((openMode & O_NOCACHE) != 0)
1963 RETURN_ERROR(B_UNSUPPORTED);
1964
1965 if (is_filename_legal(name) != true) {
1966 INFORM("invalid FAT file name '%s'\n", name);
1967 RETURN_ERROR(B_UNSUPPORTED);
1968 }
1969
1970 bool removed = false;
1971 status_t status = get_vnode_removed(volume, fatDir->de_inode, &removed);
1972 if (status == B_OK && removed == true)
1973 RETURN_ERROR(B_ENTRY_NOT_FOUND);
1974
1975 if ((openMode & O_RWMASK) == O_RDONLY)
1976 RETURN_ERROR(B_NOT_ALLOWED);
1977
1978 FileCookie* cookie = new(std::nothrow) FileCookie;
1979 if (cookie == NULL)
1980 RETURN_ERROR(B_NO_MEMORY);
1981 ObjectDeleter<FileCookie> cookieDeleter(cookie);
1982
1983 // In addition to checking for an existing file with this name, msdosfs_lookup_ino
1984 // will set de_fndoffset of the parent node to a vacant direntry slot if there is
1985 // no existing file, in preparation for createde.
1986 daddr_t cluster;
1987 u_long offset;
1988 status
1989 = B_FROM_POSIX_ERROR(msdosfs_lookup_ino(bsdDir, NULL, bsdName.Data(), &cluster, &offset));
1990
1991 if (status == B_OK) {
1992 // there is already a file with this name
1993 vnode* existingBsdNode;
1994 status = assign_inode_and_get(bsdVolume, cluster, offset, &existingBsdNode);
1995 if (status != B_OK)
1996 RETURN_ERROR(status);
1997 WriteLocker existingLocker(existingBsdNode->v_vnlock->haikuRW);
1998 NodePutter existingPutter(existingBsdNode);
1999 denode* existingFatNode = reinterpret_cast<denode*>(existingBsdNode->v_data);
2000
2001 if ((openMode & O_EXCL) != 0)
2002 RETURN_ERROR(B_FILE_EXISTS);
2003 if (existingBsdNode->v_type == VDIR)
2004 RETURN_ERROR(B_NOT_ALLOWED);
2005 if ((openMode & O_TRUNC) != 0) {
2006 status = _dosfs_access(bsdVolume, existingBsdNode, open_mode_to_access(openMode));
2007 if (status != B_OK)
2008 RETURN_ERROR(status);
2009 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW);
2010 status = B_FROM_POSIX_ERROR(detrunc(existingFatNode, 0, 0, NOCRED));
2011 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW);
2012 if (status != B_OK)
2013 RETURN_ERROR(status);
2014
2015 existingLocker.Unlock();
2016 // avoid deadlock that can happen when reducing cache size
2017 file_cache_set_size(existingBsdNode->v_cache, 0);
2018
2019 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0)
2020 _dosfs_fsync(existingBsdNode);
2021 } else {
2022 status = _dosfs_access(bsdVolume, existingBsdNode, open_mode_to_access(openMode));
2023 if (status != B_OK)
2024 RETURN_ERROR(status);
2025 }
2026
2027 *_newVnodeID = existingFatNode->de_inode;
2028
2029 cookie->fMode = openMode;
2030 cookie->fLastSize = existingFatNode->de_FileSize;
2031 cookie->fMtimeAtOpen = existingFatNode->de_MTime;
2032 cookie->fMdateAtOpen = existingFatNode->de_MDate;
2033 cookie->fLastNotification = 0;
2034 *_cookie = cookie;
2035 cookieDeleter.Detach();
2036
2037 return B_OK;
2038 }
2039
2040 if (status != B_FROM_POSIX_ERROR(EJUSTRETURN))
2041 return status;
2042
2043 // If this is the FAT12/16 root directory and there is no space left we can't do anything.
2044 // This is because the root directory can not change size.
2045 if (fatDir->de_StartCluster == MSDOSFSROOT && fatDir->de_fndoffset >= fatDir->de_FileSize) {
2046 INFORM("root directory is full and cannot be expanded\n");
2047 return B_UNSUPPORTED;
2048 }
2049
2050 // set up a dummy node that will be converted into a direntry
2051 denode newDirentry;
2052 memset(&newDirentry, 0, sizeof(newDirentry));
2053 status = B_FROM_POSIX_ERROR(uniqdosname(fatDir, bsdName.Data(), newDirentry.de_Name));
2054 if (status != B_OK)
2055 return status;
2056 if (is_shortname_legal(newDirentry.de_Name) == false) {
2057 INFORM("invalid FAT short file name '%s'\n", name);
2058 RETURN_ERROR(B_UNSUPPORTED);
2059 }
2060 newDirentry.de_Attributes = ATTR_ARCHIVE;
2061 if ((perms & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0)
2062 newDirentry.de_Attributes |= ATTR_READONLY;
2063 newDirentry.de_LowerCase = 0;
2064 newDirentry.de_StartCluster = 0;
2065 newDirentry.de_FileSize = 0;
2066 newDirentry.de_pmp = fatDir->de_pmp;
2067 newDirentry.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
2068 timespec timeSpec;
2069 vfs_timestamp(&timeSpec);
2070 DETIMES(&newDirentry, &timeSpec, &timeSpec, &timeSpec);
2071
2072 // write the direntry
2073 u_long fndoffset = fatDir->de_fndoffset;
2074 // remember this value, because fatDir->de_fndoffset is liable to change during createde
2075 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW);
2076 status = B_FROM_POSIX_ERROR(createde(&newDirentry, fatDir, NULL, bsdName.Data()));
2077 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW);
2078 if (status != B_OK)
2079 RETURN_ERROR(status);
2080
2081 // determine the inode number
2082 u_long newCluster;
2083 status = B_FROM_POSIX_ERROR(
2084 pcbmap(fatDir, de_cluster(fatVolume, fndoffset), NULL, &newCluster, NULL));
2085 if (status != B_OK)
2086 RETURN_ERROR(status);
2087 uint32 newOffset = fndoffset;
2088 if (newCluster != MSDOSFSROOT)
2089 newOffset = fndoffset % fatVolume->pm_bpcluster;
2090 ino_t inode = DETOI(fatVolume, newCluster, newOffset);
2091 status = assign_inode(bsdVolume, &inode);
2092 if (status != B_OK)
2093 RETURN_ERROR(status);
2094
2095 // set up the actual node
2096 vnode* bsdNode;
2097 status = _dosfs_read_vnode(bsdVolume, inode, &bsdNode, false);
2098 if (status != B_OK)
2099 RETURN_ERROR(status);
2100 mode_t nodeType = 0;
2101 if (bsdNode->v_type == VDIR)
2102 nodeType = S_IFDIR;
2103 else if (bsdNode->v_type == VREG)
2104 nodeType = S_IFREG;
2105 else
2106 panic("dosfs_create: unknown node type\n");
2107
2108 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
2109
2110 cookie->fMode = openMode;
2111 cookie->fLastSize = fatNode->de_FileSize;
2112 cookie->fMtimeAtOpen = fatNode->de_MTime;
2113 cookie->fMdateAtOpen = fatNode->de_MDate;
2114 cookie->fLastNotification = 0;
2115 *_cookie = cookie;
2116
2117 status = publish_vnode(volume, inode, bsdNode, &gFATVnodeOps, nodeType, 0);
2118
2119 // This is usually done in _dosfs_read_vnode. However, the node wasn't published yet,
2120 // so it would not have worked there.
2121 bsdNode->v_cache
2122 = file_cache_create(fatVolume->pm_dev->si_id, fatNode->de_inode, fatNode->de_FileSize);
2123 bsdNode->v_file_map
2124 = file_map_create(fatVolume->pm_dev->si_id, fatNode->de_inode, fatNode->de_FileSize);
2125
2126 ASSERT(static_cast<ino_t>(fatNode->de_inode) == inode);
2127 *_newVnodeID = fatNode->de_inode;
2128
2129 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0)
2130 _dosfs_fsync(bsdNode);
2131
2132 entry_cache_add(volume->id, fatDir->de_inode, name, fatNode->de_inode);
2133 notify_entry_created(volume->id, fatDir->de_inode, name, fatNode->de_inode);
2134
2135 cookieDeleter.Detach();
2136
2137 return B_OK;
2138 }
2139
2140
2141 status_t
dosfs_open(fs_volume * volume,fs_vnode * vnode,int openMode,void ** _cookie)2142 dosfs_open(fs_volume* volume, fs_vnode* vnode, int openMode, void** _cookie)
2143 {
2144 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
2145 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
2146 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
2147 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
2148
2149 FUNCTION_START("node %" B_PRIu64 " @ %p, omode %o\n", fatNode->de_inode, bsdNode, openMode);
2150
2151 *_cookie = NULL;
2152
2153 if ((openMode & O_NOCACHE) != 0)
2154 RETURN_ERROR(B_UNSUPPORTED);
2155
2156 if ((openMode & O_CREAT) != 0) {
2157 PRINT("dosfs_open called with O_CREAT. call dosfs_create instead!\n");
2158 return B_BAD_VALUE;
2159 }
2160
2161 ReadLocker readLocker;
2162 WriteLocker writeLocker;
2163 if ((openMode & O_TRUNC) != 0)
2164 writeLocker.SetTo(bsdNode->v_vnlock->haikuRW, false);
2165 else
2166 readLocker.SetTo(bsdNode->v_vnlock->haikuRW, false);
2167
2168 // Opening a directory read-only is allowed, although you can't read
2169 // any data from it.
2170 if (bsdNode->v_type == VDIR && (openMode & O_RWMASK) != O_RDONLY)
2171 return B_IS_A_DIRECTORY;
2172 if ((openMode & O_DIRECTORY) != 0 && bsdNode->v_type != VDIR)
2173 return B_NOT_A_DIRECTORY;
2174
2175 if ((bsdVolume->mnt_flag & MNT_RDONLY) != 0 || (fatNode->de_Attributes & ATTR_READONLY) != 0)
2176 openMode = (openMode & ~O_RWMASK) | O_RDONLY;
2177
2178 if ((openMode & O_TRUNC) != 0 && (openMode & O_RWMASK) == O_RDONLY)
2179 return B_NOT_ALLOWED;
2180
2181 status_t status = _dosfs_access(bsdVolume, bsdNode, open_mode_to_access(openMode));
2182 if (status != B_OK)
2183 RETURN_ERROR(status);
2184
2185 FileCookie* cookie = new(std::nothrow) FileCookie;
2186 if (cookie == NULL)
2187 RETURN_ERROR(B_NO_MEMORY);
2188 ObjectDeleter<FileCookie> cookieDeleter(cookie);
2189 cookie->fMode = openMode;
2190 cookie->fLastSize = fatNode->de_FileSize;
2191 cookie->fMtimeAtOpen = fatNode->de_MTime;
2192 cookie->fMdateAtOpen = fatNode->de_MDate;
2193 cookie->fLastNotification = 0;
2194 *_cookie = cookie;
2195
2196 if ((openMode & O_TRUNC) != 0) {
2197 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW);
2198 status = B_FROM_POSIX_ERROR(detrunc(fatNode, 0, 0, NOCRED));
2199 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW);
2200 if (status != B_OK)
2201 RETURN_ERROR(status);
2202
2203 writeLocker.Unlock();
2204 status = file_cache_set_size(bsdNode->v_cache, 0);
2205 if (status != B_OK)
2206 RETURN_ERROR(status);
2207 }
2208
2209 cookieDeleter.Detach();
2210
2211 return B_OK;
2212 }
2213
2214
2215 static status_t
dosfs_close(fs_volume * volume,fs_vnode * vnode,void * cookie)2216 dosfs_close(fs_volume* volume, fs_vnode* vnode, void* cookie)
2217 {
2218 FUNCTION_START("%p\n", vnode->private_node);
2219
2220 return B_OK;
2221 }
2222
2223
2224 static status_t
dosfs_free_cookie(fs_volume * volume,fs_vnode * vnode,void * cookie)2225 dosfs_free_cookie(fs_volume* volume, fs_vnode* vnode, void* cookie)
2226 {
2227 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
2228 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
2229
2230 FUNCTION_START("%s (inode %" B_PRIu64 " at %p)\n", fatNode->de_Name, fatNode->de_inode,
2231 bsdNode);
2232
2233 ReadLocker readLocker;
2234 WriteLocker writeLocker;
2235 bool correctLock = false;
2236 while (correctLock == false) {
2237 if ((fatNode->de_flag & (DE_UPDATE | DE_ACCESS | DE_CREATE)) != 0) {
2238 writeLocker.SetTo(bsdNode->v_vnlock->haikuRW, false);
2239 if ((fatNode->de_flag & (DE_UPDATE | DE_ACCESS | DE_CREATE)) != 0)
2240 correctLock = true;
2241 else
2242 writeLocker.Unlock();
2243 } else {
2244 readLocker.SetTo(bsdNode->v_vnlock->haikuRW, false);
2245 if ((fatNode->de_flag & (DE_UPDATE | DE_ACCESS | DE_CREATE)) == 0)
2246 correctLock = true;
2247 else
2248 readLocker.Unlock();
2249 }
2250 }
2251
2252 struct timespec timeSpec;
2253 vfs_timestamp(&timeSpec);
2254 DETIMES(fatNode, &timeSpec, &timeSpec, &timeSpec);
2255
2256 FileCookie* fatCookie = reinterpret_cast<FileCookie*>(cookie);
2257 bool changedSize = fatCookie->fLastSize != fatNode->de_FileSize ? true : false;
2258 bool changedTime = false;
2259 if (fatCookie->fMtimeAtOpen != fatNode->de_MTime
2260 || fatCookie->fMdateAtOpen != fatNode->de_MDate) {
2261 changedTime = true;
2262 }
2263 if (changedSize || changedTime) {
2264 notify_stat_changed(volume->id, bsdNode->v_parent, fatNode->de_inode,
2265 (changedTime ? B_STAT_MODIFICATION_TIME : 0) | (changedSize ? B_STAT_SIZE : 0));
2266 }
2267
2268 if ((bsdNode->v_mount->mnt_flag & MNT_SYNCHRONOUS) != 0)
2269 deupdat(fatNode, 1);
2270
2271 delete fatCookie;
2272
2273 return B_OK;
2274 }
2275
2276
2277 status_t
dosfs_read(fs_volume * volume,fs_vnode * vnode,void * cookie,off_t pos,void * buffer,size_t * length)2278 dosfs_read(fs_volume* volume, fs_vnode* vnode, void* cookie, off_t pos, void* buffer,
2279 size_t* length)
2280 {
2281 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
2282
2283 FileCookie* fatCookie = reinterpret_cast<FileCookie*>(cookie);
2284
2285 FUNCTION_START("%" B_PRIuSIZE " bytes at %" B_PRIdOFF " (node %" B_PRIu64 " @ %p)\n", *length,
2286 pos, reinterpret_cast<denode*>(bsdNode->v_data)->de_inode, bsdNode);
2287
2288 if ((bsdNode->v_type & VDIR) != 0) {
2289 *length = 0;
2290 return B_IS_A_DIRECTORY;
2291 }
2292
2293 if ((fatCookie->fMode & O_RWMASK) == O_WRONLY) {
2294 *length = 0;
2295 RETURN_ERROR(B_NOT_ALLOWED);
2296 }
2297
2298 // The userlandfs implementation of file_cache_read seems to rely on the FS to decide
2299 // when to stop reading - it returns B_BAD_VALUE if called again after EOF has been reached.
2300 #if USER
2301 if (static_cast<u_long>(pos) >= reinterpret_cast<denode*>(bsdNode->v_data)->de_FileSize) {
2302 *length = 0;
2303 return B_OK;
2304 }
2305 #endif
2306
2307 RETURN_ERROR(file_cache_read(bsdNode->v_cache, fatCookie, pos, buffer, length));
2308 }
2309
2310
2311 status_t
dosfs_write(fs_volume * volume,fs_vnode * vnode,void * cookie,off_t pos,const void * buffer,size_t * length)2312 dosfs_write(fs_volume* volume, fs_vnode* vnode, void* cookie, off_t pos, const void* buffer,
2313 size_t* length)
2314 {
2315 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
2316 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
2317 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
2318 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
2319
2320 if (pos < 0)
2321 return B_BAD_VALUE;
2322
2323 FileCookie* fatCookie = reinterpret_cast<FileCookie*>(cookie);
2324
2325 if ((fatCookie->fMode & O_RWMASK) == O_RDONLY)
2326 RETURN_ERROR(B_NOT_ALLOWED);
2327
2328 WriteLocker locker(bsdNode->v_vnlock->haikuRW);
2329
2330 FUNCTION_START("%" B_PRIuSIZE " bytes at %" B_PRIdOFF " from buffer at %p (vnode id %" B_PRIu64
2331 ")\n", *length, pos, buffer, fatNode->de_inode);
2332
2333 size_t origSize = fatNode->de_FileSize;
2334
2335 switch (bsdNode->v_type) {
2336 case VREG:
2337 if ((fatCookie->fMode & O_APPEND) != 0)
2338 pos = fatNode->de_FileSize;
2339 break;
2340 case VDIR:
2341 return B_IS_A_DIRECTORY;
2342 default:
2343 RETURN_ERROR(B_BAD_VALUE);
2344 }
2345
2346 // if they've exceeded their filesize limit, tell them about it
2347 if (pos >= MSDOSFS_FILESIZE_MAX)
2348 RETURN_ERROR(B_FILE_TOO_LARGE);
2349
2350 if ((pos + *length) >= MSDOSFS_FILESIZE_MAX)
2351 *length = static_cast<size_t>(MSDOSFS_FILESIZE_MAX - pos);
2352
2353 // if we write beyond the end of the file, extend it
2354 status_t status = B_OK;
2355 if (pos + (*length) > fatNode->de_FileSize) {
2356 PRINT("dosfs_write: extending %" B_PRIu64 " to %" B_PRIdOFF " > file size %lu\n",
2357 fatNode->de_inode, pos + *length, fatNode->de_FileSize);
2358
2359 bsdNode->v_resizing = true;
2360 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW);
2361 status = B_FROM_POSIX_ERROR(deextend(fatNode, static_cast<size_t>(pos) + *length, NOCRED));
2362 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW);
2363 bsdNode->v_resizing = false;
2364 // if there is not enough free space to extend as requested, we return here
2365 if (status != B_OK)
2366 RETURN_ERROR(status);
2367
2368 PRINT("setting file size to %lu (%lu clusters)\n", fatNode->de_FileSize,
2369 de_clcount(fatVolume, fatNode->de_FileSize));
2370 ASSERT(fatNode->de_FileSize == static_cast<unsigned long>(pos) + *length);
2371 }
2372
2373 locker.Unlock();
2374 status = file_cache_set_size(bsdNode->v_cache, fatNode->de_FileSize);
2375 if (status == B_OK) {
2376 status = file_cache_write(bsdNode->v_cache, fatCookie, pos, buffer, length);
2377 if (status != B_OK) {
2378 REPORT_ERROR(status);
2379 status = B_OK;
2380 }
2381 if (*length == 0)
2382 status = B_IO_ERROR;
2383 }
2384 if (status != B_OK) {
2385 // complete write failure
2386 if (origSize < fatNode->de_FileSize) {
2387 // return file to its previous size
2388 int truncFlag = ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0) ? IO_SYNC : 0;
2389 locker.Lock();
2390 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW);
2391 status_t undoStatus = B_FROM_POSIX_ERROR(detrunc(fatNode, origSize, truncFlag, NOCRED));
2392 if (undoStatus != 0)
2393 REPORT_ERROR(undoStatus);
2394 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW);
2395 locker.Unlock();
2396 file_cache_set_size(bsdNode->v_cache, origSize);
2397 }
2398 RETURN_ERROR(status);
2399 }
2400
2401 // do the zeroing that is disabled in deextend
2402 if (static_cast<u_long>(pos) > origSize) {
2403 status = fill_gap_with_zeros(bsdNode, origSize, pos);
2404 if (status != B_OK)
2405 REPORT_ERROR(status);
2406 }
2407
2408 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0) {
2409 status = _dosfs_fsync(bsdNode);
2410 if (status != B_OK)
2411 REPORT_ERROR(status);
2412 }
2413
2414 if (fatNode->de_FileSize > 0 && fatNode->de_FileSize > fatCookie->fLastSize
2415 && system_time() > fatCookie->fLastNotification + INODE_NOTIFICATION_INTERVAL) {
2416 notify_stat_changed(volume->id, bsdNode->v_parent, fatNode->de_inode,
2417 B_STAT_MODIFICATION_TIME | B_STAT_SIZE | B_STAT_INTERIM_UPDATE);
2418 fatCookie->fLastSize = fatNode->de_FileSize;
2419 fatCookie->fLastNotification = system_time();
2420 }
2421
2422 return B_OK;
2423 }
2424
2425
2426 static status_t
dosfs_mkdir(fs_volume * volume,fs_vnode * parent,const char * name,int perms)2427 dosfs_mkdir(fs_volume* volume, fs_vnode* parent, const char* name, int perms)
2428 {
2429 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
2430 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
2431 vnode* bsdParent = reinterpret_cast<vnode*>(parent->private_node);
2432 denode* fatParent = reinterpret_cast<denode*>(bsdParent->v_data);
2433
2434 FUNCTION_START("%" B_PRIu64 "/%s (perm %o)\n", fatParent->de_inode, name, perms);
2435
2436 if (is_filename_legal(name) == false)
2437 RETURN_ERROR(B_BAD_VALUE);
2438
2439 ComponentName bsdName(ISLASTCN, NOCRED, CREATE, 0, name);
2440
2441 WriteLocker locker(bsdParent->v_vnlock->haikuRW);
2442
2443 status_t status = _dosfs_access(bsdVolume, bsdParent, W_OK);
2444 if (status != B_OK)
2445 RETURN_ERROR(status);
2446
2447 if (bsdParent->v_type != VDIR)
2448 return B_BAD_TYPE;
2449
2450 bool removed = false;
2451 status = get_vnode_removed(volume, fatParent->de_inode, &removed);
2452 if (status == B_OK && removed == true)
2453 RETURN_ERROR(B_ENTRY_NOT_FOUND);
2454
2455 // add file type information to perms
2456 perms &= ~S_IFMT;
2457 perms |= S_IFDIR;
2458
2459 // set fatParent::de_fndoffset and de_fndcnt in preparation for createde
2460 vnode* existingBsdNode;
2461 status = msdosfs_lookup_ino(bsdParent, &existingBsdNode, bsdName.Data(), NULL, NULL);
2462 if (status == 0) {
2463 // a directory with this name already exists
2464 rw_lock_write_unlock(&existingBsdNode->v_vnlock->haikuRW);
2465 put_vnode(volume, (reinterpret_cast<denode*>(existingBsdNode->v_data))->de_inode);
2466 return B_FILE_EXISTS;
2467 }
2468 if (status != EJUSTRETURN)
2469 RETURN_ERROR(B_FROM_POSIX_ERROR(status));
2470
2471 // If this is the FAT12/16 root directory and there is no space left we can't do anything.
2472 // This is because the root directory can not change size.
2473 if (fatParent->de_StartCluster == MSDOSFSROOT
2474 && fatParent->de_fndoffset >= fatParent->de_FileSize) {
2475 INFORM("root directory is full and cannot be expanded\n");
2476 return B_UNSUPPORTED;
2477 }
2478
2479 // allocate a cluster to hold the about to be created directory
2480 u_long newCluster;
2481 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW);
2482 status = B_FROM_POSIX_ERROR(clusteralloc(fatVolume, 0, 1, CLUST_EOFE, &newCluster, NULL));
2483 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW);
2484 if (status != B_OK)
2485 RETURN_ERROR(status);
2486
2487 // start setting up a dummy node to convert to the new direntry
2488 denode newEntry;
2489 memset(&newEntry, 0, sizeof(newEntry));
2490 newEntry.de_pmp = fatVolume;
2491 newEntry.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE;
2492 timespec timeSpec;
2493 vfs_timestamp(&timeSpec);
2494 DETIMES(&newEntry, &timeSpec, &timeSpec, &timeSpec);
2495
2496 // Now fill the cluster with the "." and ".." entries. And write the cluster to disk. This
2497 // way it is there for the parent directory to be pointing at if there were a crash.
2498 off_t startBlock = cntobn(fatVolume, newCluster);
2499 buf* newData = getblk(fatVolume->pm_devvp, startBlock, fatVolume->pm_bpcluster, 0, 0, 0);
2500 if (newData == NULL) {
2501 clusterfree(fatVolume, newCluster);
2502 RETURN_ERROR(B_ERROR);
2503 }
2504 // clear out rest of cluster to keep scandisk happy
2505 memset(newData->b_data, 0, fatVolume->pm_bpcluster);
2506 memcpy(newData->b_data, &gDirTemplate, sizeof gDirTemplate);
2507 direntry* childEntries = reinterpret_cast<direntry*>(newData->b_data);
2508 putushort(childEntries[0].deStartCluster, newCluster);
2509 putushort(childEntries[0].deCDate, newEntry.de_CDate);
2510 putushort(childEntries[0].deCTime, newEntry.de_CTime);
2511 childEntries[0].deCHundredth = newEntry.de_CHun;
2512 putushort(childEntries[0].deADate, newEntry.de_ADate);
2513 putushort(childEntries[0].deMDate, newEntry.de_MDate);
2514 putushort(childEntries[0].deMTime, newEntry.de_MTime);
2515 u_long parentCluster = fatParent->de_StartCluster;
2516 // Although the root directory has a non-magic starting cluster number for FAT32, chkdsk and
2517 // fsck_msdosfs still require references to it in dotdot entries to be magic.
2518 if (FAT32(fatVolume) == true && parentCluster == fatVolume->pm_rootdirblk)
2519 parentCluster = MSDOSFSROOT;
2520 putushort(childEntries[1].deStartCluster, parentCluster);
2521 putushort(childEntries[1].deCDate, newEntry.de_CDate);
2522 putushort(childEntries[1].deCTime, newEntry.de_CTime);
2523 childEntries[1].deCHundredth = newEntry.de_CHun;
2524 putushort(childEntries[1].deADate, newEntry.de_ADate);
2525 putushort(childEntries[1].deMDate, newEntry.de_MDate);
2526 putushort(childEntries[1].deMTime, newEntry.de_MTime);
2527 if (FAT32(fatVolume) == true) {
2528 putushort(childEntries[0].deHighClust, newCluster >> 16);
2529 putushort(childEntries[1].deHighClust, parentCluster >> 16);
2530 }
2531
2532 if (DOINGASYNC(bsdParent) == true) {
2533 bdwrite(newData);
2534 } else if ((status = B_FROM_POSIX_ERROR(bwrite(newData))) != B_OK) {
2535 clusterfree(fatVolume, newCluster);
2536 RETURN_ERROR(status);
2537 }
2538
2539 // Now build up a directory entry pointing to the newly allocated cluster. This will be
2540 // written to an empty slot in the parent directory.
2541 status = B_FROM_POSIX_ERROR(uniqdosname(fatParent, bsdName.Data(), newEntry.de_Name));
2542 if (status == B_OK && is_shortname_legal(newEntry.de_Name) == false)
2543 status = B_UNSUPPORTED;
2544 if (status != B_OK) {
2545 clusterfree(fatVolume, newCluster);
2546 RETURN_ERROR(status);
2547 }
2548
2549 newEntry.de_Attributes = ATTR_DIRECTORY;
2550 // The FAT on-disk direntry is limited in the permissions it can store
2551 if ((perms & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0)
2552 newEntry.de_Attributes |= ATTR_READONLY;
2553 newEntry.de_LowerCase = 0;
2554 newEntry.de_StartCluster = newCluster;
2555 newEntry.de_FileSize = 0;
2556
2557 // convert newEntry into a new direntry and write it
2558 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW);
2559 // lock FAT in case parent must be extended to hold another direntry
2560 status = B_FROM_POSIX_ERROR(createde(&newEntry, fatParent, NULL, bsdName.Data()));
2561 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW);
2562 if (status != B_OK) {
2563 clusterfree(fatVolume, newCluster);
2564 RETURN_ERROR(status);
2565 }
2566
2567 // set up the actual node
2568 ino_t inode = DETOI(fatVolume, newCluster, 0);
2569 assign_inode(bsdVolume, &inode);
2570 vnode* bsdNode;
2571 status = _dosfs_read_vnode(bsdVolume, inode, &bsdNode);
2572 if (status != B_OK) {
2573 clusterfree(fatVolume, newCluster);
2574 RETURN_ERROR(status);
2575 }
2576 // parent is not accessible in _dosfs_read_vnode when the node is a directory.
2577 bsdNode->v_parent = fatParent->de_inode;
2578
2579 status = publish_vnode(volume, inode, bsdNode, &gFATVnodeOps, S_IFDIR, 0);
2580 if (status != B_OK) {
2581 clusterfree(fatVolume, newCluster);
2582 RETURN_ERROR(status);
2583 }
2584
2585 put_vnode(volume, inode);
2586
2587 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0)
2588 _dosfs_fsync(bsdNode);
2589
2590 entry_cache_add(volume->id, fatParent->de_inode, name, inode);
2591
2592 notify_entry_created(volume->id, fatParent->de_inode, name, inode);
2593
2594 return B_OK;
2595 }
2596
2597
2598 static status_t
dosfs_rmdir(fs_volume * volume,fs_vnode * parent,const char * name)2599 dosfs_rmdir(fs_volume* volume, fs_vnode* parent, const char* name)
2600 {
2601 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
2602 vnode* bsdParent = reinterpret_cast<vnode*>(parent->private_node);
2603 denode* fatParent = reinterpret_cast<denode*>(bsdParent->v_data);
2604
2605 FUNCTION_START("%s in %" B_PRIu64 " at %p\n", name, fatParent->de_inode, bsdParent);
2606
2607 if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
2608 return B_NOT_ALLOWED;
2609
2610 ComponentName bsdName(ISLASTCN, NOCRED, DELETE, 0, name);
2611
2612 WriteLocker parentLocker(bsdParent->v_vnlock->haikuRW);
2613
2614 daddr_t cluster;
2615 u_long offset;
2616 status_t status = B_FROM_POSIX_ERROR(
2617 msdosfs_lookup_ino(bsdParent, NULL, bsdName.Data(), &cluster, &offset));
2618 if (status != B_OK)
2619 RETURN_ERROR(status);
2620
2621 vnode* bsdTarget;
2622 status = assign_inode_and_get(bsdVolume, cluster, offset, &bsdTarget);
2623 if (status != B_OK)
2624 RETURN_ERROR(status);
2625 WriteLocker targetLocker(bsdTarget->v_vnlock->haikuRW);
2626 NodePutter targetPutter(bsdTarget);
2627 denode* fatTarget = reinterpret_cast<denode*>(bsdTarget->v_data);
2628
2629 if (bsdTarget->v_type != VDIR)
2630 return B_NOT_A_DIRECTORY;
2631 if ((bsdTarget->v_vflag & VV_ROOT) != 0)
2632 return B_NOT_ALLOWED;
2633 if (dosdirempty(fatTarget) == false)
2634 return B_DIRECTORY_NOT_EMPTY;
2635
2636 status = _dosfs_access(bsdVolume, bsdTarget, W_OK);
2637 if (status != B_OK)
2638 RETURN_ERROR(status);
2639
2640 status = B_FROM_POSIX_ERROR(removede(fatParent, fatTarget));
2641 if (status != B_OK)
2642 RETURN_ERROR(status);
2643
2644 // Set the loc to a unique value. This effectively removes it from the vcache without
2645 // releasing its vnid for reuse. It also nicely reserves the vnid from use by other nodes.
2646 // This is okay because the vnode is locked in memory after this point and loc will not
2647 // be referenced from here on.
2648 status = vcache_set_entry(bsdVolume, fatTarget->de_inode, generate_unique_vnid(bsdVolume));
2649 if (status != B_OK)
2650 RETURN_ERROR(status);
2651
2652 status = remove_vnode(volume, fatTarget->de_inode);
2653 if (status != B_OK)
2654 RETURN_ERROR(status);
2655
2656 targetLocker.Unlock();
2657
2658 if ((bsdVolume->mnt_flag & MNT_SYNCHRONOUS) != 0)
2659 _dosfs_sync(bsdVolume, false);
2660
2661 entry_cache_remove(volume->id, fatTarget->de_inode, "..");
2662 entry_cache_remove(volume->id, fatParent->de_inode, name);
2663
2664 notify_entry_removed(volume->id, fatParent->de_inode, name, fatTarget->de_inode);
2665
2666 return B_OK;
2667 }
2668
2669
2670 static status_t
dosfs_opendir(fs_volume * volume,fs_vnode * vnode,void ** _cookie)2671 dosfs_opendir(fs_volume* volume, fs_vnode* vnode, void** _cookie)
2672 {
2673 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
2674
2675 FUNCTION_START("%p\n", bsdNode);
2676
2677 ReadLocker locker(bsdNode->v_vnlock->haikuRW);
2678
2679 *_cookie = NULL;
2680
2681 if ((bsdNode->v_type & VDIR) == 0)
2682 return B_NOT_A_DIRECTORY;
2683
2684 DirCookie* cookie = new(std::nothrow) DirCookie;
2685 if (cookie == NULL)
2686 RETURN_ERROR(B_NO_MEMORY);
2687
2688 cookie->fIndex = 0;
2689
2690 *_cookie = cookie;
2691
2692 return B_OK;
2693 }
2694
2695
2696 status_t
dosfs_closedir(fs_volume * volume,fs_vnode * vnode,void * cookie)2697 dosfs_closedir(fs_volume* volume, fs_vnode* vnode, void* cookie)
2698 {
2699 FUNCTION_START("%p\n", vnode->private_node);
2700
2701 return B_OK;
2702 }
2703
2704
2705 status_t
dosfs_free_dircookie(fs_volume * volume,fs_vnode * vnode,void * cookie)2706 dosfs_free_dircookie(fs_volume* volume, fs_vnode* vnode, void* cookie)
2707 {
2708 delete reinterpret_cast<DirCookie*>(cookie);
2709
2710 return B_OK;
2711 }
2712
2713
2714 static status_t
dosfs_readdir(fs_volume * volume,fs_vnode * vnode,void * cookie,struct dirent * buffer,size_t bufferSize,uint32 * _num)2715 dosfs_readdir(fs_volume* volume, fs_vnode* vnode, void* cookie, struct dirent* buffer,
2716 size_t bufferSize, uint32* _num)
2717 {
2718 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
2719 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
2720 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
2721 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
2722
2723 FUNCTION_START("vp %p(%" B_PRIu64 "), bufferSize %lu, entries to be read %" B_PRIu32 "\n",
2724 bsdNode, fatNode->de_inode, bufferSize, *_num);
2725
2726 WriteLocker locker(bsdNode->v_vnlock->haikuRW);
2727
2728 if ((fatNode->de_Attributes & ATTR_DIRECTORY) == 0)
2729 RETURN_ERROR(B_NOT_A_DIRECTORY);
2730
2731 uint32 entriesRequested = *_num;
2732 *_num = 0;
2733
2734 // struct dirent is defined differently in Haiku and FreeBSD. In the ported driver,
2735 // Haiku's definition is the relevant one.
2736 dirent* dirBuf = reinterpret_cast<dirent*>(alloca(sizeof(struct dirent) + MAXNAMLEN + 1));
2737 memset(dirBuf, 0, sizeof(struct dirent) + MAXNAMLEN + 1);
2738
2739 char* byteBuffer = reinterpret_cast<char*>(buffer);
2740
2741 // If they are reading from the root directory then, we simulate the . and .. entries since
2742 // these don't exist in the root directory. We also set the offset bias to make up for having
2743 // to simulate these entries. By this I mean that at file offset 64 we read the first entry in
2744 // the root directory that lives on disk.
2745
2746 // directory-relative index of the current direntry or winentry;
2747 // it is incremented for the simulated . and .. entries in the root directory too
2748 uint32* entryIndex = &reinterpret_cast<DirCookie*>(cookie)->fIndex;
2749
2750 int32 bias = 0;
2751 // disk offset = virtual offset - bias
2752 if (static_cast<ino_t>(fatNode->de_inode) == root_inode(fatVolume))
2753 bias += 2 * sizeof(direntry);
2754
2755 if (*entryIndex * sizeof(direntry) >= fatNode->de_FileSize + bias)
2756 return B_OK;
2757
2758 if (static_cast<ino_t>(fatNode->de_inode) == root_inode(fatVolume)) {
2759 for (; *entryIndex < 2 && *_num < entriesRequested; ++*entryIndex, ++*_num) {
2760 dirBuf->d_ino = fatNode->de_inode;
2761 dirBuf->d_dev = volume->id;
2762 switch (*entryIndex) {
2763 case 0:
2764 dirBuf->d_name[0] = '.';
2765 dirBuf->d_name[1] = '\0';
2766 break;
2767 case 1:
2768 dirBuf->d_name[0] = '.';
2769 dirBuf->d_name[1] = '.';
2770 dirBuf->d_name[2] = '\0';
2771 break;
2772 }
2773 dirBuf->d_reclen = GENERIC_DIRSIZ(dirBuf);
2774
2775 if (bufferSize < dirBuf->d_reclen) {
2776 if (*_num == 0)
2777 RETURN_ERROR(B_BUFFER_OVERFLOW)
2778 else
2779 return B_OK;
2780 }
2781
2782 memcpy(byteBuffer, dirBuf, dirBuf->d_reclen);
2783
2784 bufferSize -= dirBuf->d_reclen;
2785 byteBuffer += dirBuf->d_reclen;
2786 }
2787 }
2788
2789 buf* entriesBuf;
2790 // disk entries being read from
2791 mbnambuf longName;
2792 // filename after extraction and conversion from winentries
2793 mbnambuf_init(&longName);
2794 int chkSum = -1;
2795 // checksum of the filename
2796 int32 winChain = 0;
2797 // number of consecutive winentries we have found before reaching the corresponding
2798 // direntry
2799 bool done = false;
2800 status_t status = B_OK;
2801 while (bufferSize > 0 && *_num < entriesRequested && done == false) {
2802 int32 logicalCluster = de_cluster(fatVolume, (*entryIndex * sizeof(direntry)) - bias);
2803 // file-relative cluster number containing the next entry to read
2804 int32 clusterOffset = ((*entryIndex * sizeof(direntry)) - bias) & fatVolume->pm_crbomask;
2805 // byte offset into buf::b_data at which the inner loop starts reading
2806 int32 fileDiff = fatNode->de_FileSize - (*entryIndex * sizeof(direntry) - bias);
2807 // remaining data in the directory file
2808 if (fileDiff <= 0)
2809 break;
2810 int32 bytesLeft
2811 = min_c(static_cast<int32>(fatVolume->pm_bpcluster) - clusterOffset, fileDiff);
2812 // remaining data in the struct buf, excluding any area past EOF
2813
2814 int readSize;
2815 // how many bytes to read into the struct buf at a time; usually cluster size but
2816 // 512 bytes for the FAT12/16 root directory
2817 daddr_t readBlock;
2818 // volume-relative index of the readSize-sized block into entriesBuf
2819 u_long volumeCluster;
2820 // volume-relative cluster number containing the next entry to read
2821 status = B_FROM_POSIX_ERROR(
2822 pcbmap(fatNode, logicalCluster, &readBlock, &volumeCluster, &readSize));
2823 if (status != B_OK)
2824 break;
2825
2826 status = B_FROM_POSIX_ERROR(
2827 bread(fatVolume->pm_devvp, readBlock, readSize, NOCRED, &entriesBuf));
2828 if (status != B_OK)
2829 break;
2830
2831 bytesLeft = min_c(bytesLeft, readSize - entriesBuf->b_resid);
2832 if (bytesLeft == 0) {
2833 brelse(entriesBuf);
2834 status = B_IO_ERROR;
2835 break;
2836 }
2837
2838 // convert from DOS directory entries to FS-independent directory entries
2839 direntry* fatEntry;
2840 for (fatEntry = reinterpret_cast<direntry*>(entriesBuf->b_data + clusterOffset);
2841 reinterpret_cast<char*>(fatEntry) < entriesBuf->b_data + clusterOffset + bytesLeft
2842 && *_num < entriesRequested; fatEntry++, (*entryIndex)++) {
2843 // fatEntry is assumed to point to a struct direntry for now, but it may in fact
2844 // be a struct winentry; that case will handled below
2845
2846 // ff this is an unused entry, we can stop
2847 if (fatEntry->deName[0] == SLOT_EMPTY) {
2848 done = true;
2849 break;
2850 }
2851
2852 // skip deleted entries
2853 if (fatEntry->deName[0] == SLOT_DELETED) {
2854 chkSum = -1;
2855 mbnambuf_init(&longName);
2856 continue;
2857 }
2858
2859 // handle Win95 long directory entries
2860 if (fatEntry->deAttributes == ATTR_WIN95) {
2861 chkSum = win2unixfn(&longName, reinterpret_cast<winentry*>(fatEntry), chkSum,
2862 fatVolume);
2863 #ifdef DEBUG
2864 dprintf_winentry(fatVolume, reinterpret_cast<winentry*>(fatEntry), entryIndex);
2865 #endif
2866 winChain++;
2867 continue;
2868 }
2869
2870 // skip volume labels
2871 if (fatEntry->deAttributes & ATTR_VOLUME) {
2872 chkSum = -1;
2873 mbnambuf_init(&longName);
2874 continue;
2875 }
2876
2877 // Found a direntry. First, populate d_ino.
2878 ino_t ino;
2879 if (fatEntry->deAttributes & ATTR_DIRECTORY) {
2880 u_long entryCluster = getushort(fatEntry->deStartCluster);
2881 if (FAT32(fatVolume) != 0)
2882 entryCluster |= getushort(fatEntry->deHighClust) << 16;
2883 if (entryCluster == MSDOSFSROOT)
2884 ino = root_inode(fatVolume);
2885 else
2886 ino = DETOI(fatVolume, entryCluster, 0);
2887 } else {
2888 u_long dirOffset = *entryIndex * sizeof(direntry) - bias;
2889 if (IS_FIXED_ROOT(fatNode) == 0) {
2890 // we want a cluster-relative offset
2891 dirOffset = (dirOffset % fatVolume->pm_bpcluster);
2892 }
2893 ino = DETOI(fatVolume, volumeCluster, dirOffset);
2894 }
2895 status = assign_inode(bsdVolume, &ino);
2896 if (status != B_OK)
2897 break;
2898 dirBuf->d_ino = ino;
2899
2900
2901 dirBuf->d_dev = volume->id;
2902
2903 // Is this direntry associated with a chain of previous winentries?
2904 if (chkSum != winChksum(fatEntry->deName)) {
2905 // no, just read the short file name from this direntry
2906 dos2unixfn(fatEntry->deName, reinterpret_cast<u_char*>(dirBuf->d_name),
2907 fatEntry->deLowerCase, fatVolume);
2908 dirBuf->d_reclen = GENERIC_DIRSIZ(dirBuf);
2909 mbnambuf_init(&longName);
2910 } else {
2911 // yes, use the long file name that was assembled from the previous winentry/ies
2912 mbnambuf_flush(&longName, dirBuf);
2913 }
2914 chkSum = -1;
2915
2916 if (bufferSize < dirBuf->d_reclen) {
2917 if (*_num == 0) {
2918 RETURN_ERROR(B_BUFFER_OVERFLOW);
2919 } else {
2920 done = true;
2921 // rewind to the start of the chain of winentries that precedes this direntry
2922 *entryIndex -= winChain;
2923 break;
2924 }
2925 }
2926 winChain = 0;
2927
2928 memcpy(byteBuffer, dirBuf, dirBuf->d_reclen);
2929
2930 // A single VFS dirent corresponds to 0 or more FAT winentries plus 1 FAT direntry.
2931 // Iteration code associated with direntries is placed here, instead of in the for
2932 // loop header, so it won't execute when the for loop continues early
2933 // after a winentry is found.
2934 bufferSize -= dirBuf->d_reclen;
2935 byteBuffer += dirBuf->d_reclen;
2936 ++*_num;
2937 }
2938 brelse(entriesBuf);
2939 }
2940
2941 #ifdef DEBUG
2942 PRINT("dosfs_readdir returning %" B_PRIu32 " dirents:\n", *_num);
2943 uint8* printCursor = reinterpret_cast<uint8*>(buffer);
2944 for (uint32 i = 0; i < *_num; i++) {
2945 dirent* bufferSlot = reinterpret_cast<dirent*>(printCursor);
2946 PRINT("buffer offset: %ld, d_dev: %" B_PRIdDEV ", d_ino: %" B_PRIdINO
2947 ", d_name: %s, d_reclen: %d\n", bufferSlot - buffer, bufferSlot->d_dev,
2948 bufferSlot->d_ino, bufferSlot->d_name, bufferSlot->d_reclen);
2949 printCursor += bufferSlot->d_reclen;
2950 }
2951 #endif
2952
2953 RETURN_ERROR(status);
2954 }
2955
2956
2957 static status_t
dosfs_rewinddir(fs_volume * volume,fs_vnode * vnode,void * cookie)2958 dosfs_rewinddir(fs_volume* volume, fs_vnode* vnode, void* cookie)
2959 {
2960 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
2961 DirCookie* fatCookie = reinterpret_cast<DirCookie*>(cookie);
2962
2963 FUNCTION_START("%p\n", bsdNode);
2964
2965 WriteLocker locker(bsdNode->v_vnlock->haikuRW);
2966
2967 fatCookie->fIndex = 0;
2968
2969 return B_OK;
2970 }
2971
2972
2973 static status_t
dosfs_open_attrdir(fs_volume * volume,fs_vnode * vnode,void ** _cookie)2974 dosfs_open_attrdir(fs_volume* volume, fs_vnode* vnode, void** _cookie)
2975 {
2976 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
2977 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
2978
2979 FUNCTION_START("%p\n", bsdNode);
2980
2981 if (_dosfs_access(bsdVolume, bsdNode, O_RDONLY) != B_OK)
2982 RETURN_ERROR(B_NOT_ALLOWED);
2983
2984 if ((*_cookie = new(std::nothrow) int32) == NULL)
2985 RETURN_ERROR(B_NO_MEMORY);
2986
2987 *reinterpret_cast<int32*>(*_cookie) = 0;
2988
2989 return B_OK;
2990 }
2991
2992
2993 static status_t
dosfs_close_attrdir(fs_volume * volume,fs_vnode * vnode,void * cookie)2994 dosfs_close_attrdir(fs_volume* volume, fs_vnode* vnode, void* cookie)
2995 {
2996 FUNCTION_START("%p\n", vnode->private_node);
2997
2998 *reinterpret_cast<int32*>(cookie) = 1;
2999
3000 return B_OK;
3001 }
3002
3003
3004 static status_t
dosfs_free_attrdir_cookie(fs_volume * volume,fs_vnode * vnode,void * cookie)3005 dosfs_free_attrdir_cookie(fs_volume* volume, fs_vnode* vnode, void* cookie)
3006 {
3007 FUNCTION_START("%p\n", vnode->private_node);
3008
3009 if (cookie == NULL)
3010 return B_BAD_VALUE;
3011
3012 delete reinterpret_cast<int32*>(cookie);
3013
3014 return B_OK;
3015 }
3016
3017
3018 static status_t
dosfs_read_attrdir(fs_volume * volume,fs_vnode * vnode,void * cookie,struct dirent * buffer,size_t bufferSize,uint32 * _num)3019 dosfs_read_attrdir(fs_volume* volume, fs_vnode* vnode, void* cookie, struct dirent* buffer,
3020 size_t bufferSize, uint32* _num)
3021 {
3022 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
3023 int32* fatCookie = reinterpret_cast<int32*>(cookie);
3024
3025 FUNCTION_START("%p\n", bsdNode);
3026
3027 *_num = 0;
3028
3029 ReadLocker locker(bsdNode->v_vnlock->haikuRW);
3030
3031 if ((*fatCookie == 0) && (bsdNode->v_mime != NULL)) {
3032 *_num = 1;
3033 strcpy(buffer->d_name, "BEOS:TYPE");
3034 buffer->d_reclen = offsetof(struct dirent, d_name) + 10;
3035 }
3036
3037 *fatCookie = 1;
3038
3039 return B_OK;
3040 }
3041
3042
3043 static status_t
dosfs_rewind_attrdir(fs_volume * volume,fs_vnode * vnode,void * cookie)3044 dosfs_rewind_attrdir(fs_volume* volume, fs_vnode* vnode, void* cookie)
3045 {
3046 FUNCTION_START("%p\n", vnode->private_node);
3047
3048 if (cookie == NULL)
3049 return B_BAD_VALUE;
3050
3051 *reinterpret_cast<int32*>(cookie) = 0;
3052
3053 return B_OK;
3054 }
3055
3056
3057 static status_t
dosfs_create_attr(fs_volume * volume,fs_vnode * vnode,const char * name,uint32 type,int openMode,void ** _cookie)3058 dosfs_create_attr(fs_volume* volume, fs_vnode* vnode, const char* name, uint32 type, int openMode,
3059 void** _cookie)
3060 {
3061 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
3062 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
3063
3064 FUNCTION_START("%p\n", bsdNode);
3065
3066 ReadLocker locker(bsdNode->v_vnlock->haikuRW);
3067
3068 if (_dosfs_access(bsdVolume, bsdNode, open_mode_to_access(openMode)) != B_OK)
3069 RETURN_ERROR(B_NOT_ALLOWED);
3070
3071 if (strcmp(name, "BEOS:TYPE") != 0)
3072 return B_UNSUPPORTED;
3073
3074 if (bsdNode->v_mime == NULL)
3075 return B_BAD_VALUE;
3076
3077 AttrCookie* cookie = new(std::nothrow) AttrCookie;
3078 cookie->fMode = openMode;
3079 cookie->fType = FAT_ATTR_MIME;
3080 *_cookie = cookie;
3081
3082 return B_OK;
3083 }
3084
3085
3086 static status_t
dosfs_open_attr(fs_volume * volume,fs_vnode * vnode,const char * name,int openMode,void ** _cookie)3087 dosfs_open_attr(fs_volume* volume, fs_vnode* vnode, const char* name, int openMode, void** _cookie)
3088 {
3089 mount* bsdVolume = reinterpret_cast<mount*>(volume->private_volume);
3090 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
3091
3092 FUNCTION_START("%p\n", bsdNode);
3093
3094 ReadLocker locker(bsdNode->v_vnlock->haikuRW);
3095
3096 if (_dosfs_access(bsdVolume, bsdNode, open_mode_to_access(openMode)) != B_OK)
3097 RETURN_ERROR(B_NOT_ALLOWED);
3098
3099 if (strcmp(name, "BEOS:TYPE") != 0)
3100 return B_UNSUPPORTED;
3101
3102 if (bsdNode->v_mime == NULL)
3103 return B_BAD_VALUE;
3104
3105 AttrCookie* cookie = new(std::nothrow) AttrCookie;
3106 cookie->fMode = openMode;
3107 cookie->fType = FAT_ATTR_MIME;
3108 *_cookie = cookie;
3109
3110 return B_OK;
3111 }
3112
3113
3114 static status_t
dosfs_close_attr(fs_volume * volume,fs_vnode * vnode,void * cookie)3115 dosfs_close_attr(fs_volume* volume, fs_vnode* vnode, void* cookie)
3116 {
3117 return B_OK;
3118 }
3119
3120
3121 static status_t
dosfs_free_attr_cookie(fs_volume * volume,fs_vnode * vnode,void * cookie)3122 dosfs_free_attr_cookie(fs_volume* volume, fs_vnode* vnode, void* cookie)
3123 {
3124 delete reinterpret_cast<AttrCookie*>(cookie);
3125
3126 return B_OK;
3127 }
3128
3129
3130 static status_t
dosfs_read_attr(fs_volume * volume,fs_vnode * vnode,void * cookie,off_t pos,void * buffer,size_t * length)3131 dosfs_read_attr(fs_volume* volume, fs_vnode* vnode, void* cookie, off_t pos, void* buffer,
3132 size_t* length)
3133 {
3134 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
3135
3136 FUNCTION_START("%p\n", bsdNode);
3137
3138 AttrCookie* fatCookie = reinterpret_cast<AttrCookie*>(cookie);
3139 if (fatCookie->fType != FAT_ATTR_MIME)
3140 return B_NOT_ALLOWED;
3141 if ((fatCookie->fMode & O_RWMASK) == O_WRONLY)
3142 return B_NOT_ALLOWED;
3143
3144 ReadLocker locker(bsdNode->v_vnlock->haikuRW);
3145
3146 if (bsdNode->v_mime == NULL)
3147 return B_BAD_VALUE;
3148
3149 if ((pos < 0) || (pos > static_cast<off_t>(strlen(bsdNode->v_mime))))
3150 return B_BAD_VALUE;
3151
3152 ssize_t copied = user_strlcpy(reinterpret_cast<char*>(buffer),
3153 bsdNode->v_mime + pos, *length);
3154 if (copied < 0)
3155 return B_BAD_ADDRESS;
3156
3157 if (static_cast<size_t>(copied) < *length)
3158 *length = copied + 1;
3159
3160 return B_OK;
3161 }
3162
3163
3164 /*! suck up application attempts to set mime types; this hides an unsightly
3165 error message printed out by zip
3166 */
3167 static status_t
dosfs_write_attr(fs_volume * volume,fs_vnode * vnode,void * cookie,off_t pos,const void * buffer,size_t * length)3168 dosfs_write_attr(fs_volume* volume, fs_vnode* vnode, void* cookie, off_t pos, const void* buffer,
3169 size_t* length)
3170 {
3171 FUNCTION_START("%p\n", vnode->private_node);
3172
3173 AttrCookie* fatCookie = reinterpret_cast<AttrCookie*>(cookie);
3174 if (fatCookie->fType != FAT_ATTR_MIME)
3175 return B_NOT_ALLOWED;
3176 if ((fatCookie->fMode & O_RWMASK) == O_RDONLY)
3177 return B_NOT_ALLOWED;
3178
3179 return B_OK;
3180 }
3181
3182
3183 static status_t
dosfs_read_attr_stat(fs_volume * volume,fs_vnode * vnode,void * cookie,struct stat * stat)3184 dosfs_read_attr_stat(fs_volume* volume, fs_vnode* vnode, void* cookie, struct stat* stat)
3185 {
3186 struct vnode* bsdNode = reinterpret_cast<struct vnode*>(vnode->private_node);
3187
3188 FUNCTION_START("%p\n", bsdNode);
3189
3190 AttrCookie* fatCookie = reinterpret_cast<AttrCookie*>(cookie);
3191 if (fatCookie->fType != FAT_ATTR_MIME)
3192 return B_NOT_ALLOWED;
3193 if ((fatCookie->fMode & O_RWMASK) == O_WRONLY)
3194 return B_NOT_ALLOWED;
3195
3196 ReadLocker locker(bsdNode->v_vnlock->haikuRW);
3197
3198 if (bsdNode->v_mime == NULL)
3199 return B_BAD_VALUE;
3200
3201 stat->st_type = B_MIME_STRING_TYPE;
3202 stat->st_size = strlen(bsdNode->v_mime) + 1;
3203
3204 return B_OK;
3205 }
3206
3207
3208 status_t
dosfs_initialize(int fd,partition_id partitionID,const char * name,const char * parameterString,off_t partitionSize,disk_job_id job)3209 dosfs_initialize(int fd, partition_id partitionID, const char* name, const char* parameterString,
3210 off_t partitionSize, disk_job_id job)
3211 {
3212 return _dosfs_initialize(fd, partitionID, name, parameterString, partitionSize, job);
3213 }
3214
3215
3216 status_t
dosfs_uninitialize(int fd,partition_id partitionID,off_t partitionSize,uint32 blockSize,disk_job_id job)3217 dosfs_uninitialize(int fd, partition_id partitionID, off_t partitionSize, uint32 blockSize,
3218 disk_job_id job)
3219 {
3220 return _dosfs_uninitialize(fd, partitionID, partitionSize, blockSize, job);
3221 }
3222
3223
3224 /*! Initialize a FreeBSD-style struct cdev.
3225 @param _readOnly As input, reflects the user-selected mount options; as output, will be set to
3226 true if the device is read-only or device parameters are outside the driver's scope of
3227 read-write support.
3228 */
3229 static status_t
bsd_device_init(mount * bsdVolume,const dev_t devID,const char * deviceFile,cdev ** bsdDevice,bool * _readOnly)3230 bsd_device_init(mount* bsdVolume, const dev_t devID, const char* deviceFile, cdev** bsdDevice,
3231 bool* _readOnly)
3232 {
3233 cdev* device = new(std::nothrow) cdev;
3234 if (device == NULL)
3235 RETURN_ERROR(B_NO_MEMORY);
3236 ObjectDeleter<cdev> deviceDeleter(device);
3237
3238 device->si_fd = -1;
3239 device->si_refcount = 0;
3240 device->si_mountpt = bsdVolume;
3241 device->si_name[0] = '\0';
3242 strncpy(device->si_device, deviceFile, B_PATH_NAME_LENGTH - 1);
3243 device->si_mediasize = 0;
3244 device->si_id = devID;
3245
3246 device->si_geometry = new(std::nothrow) device_geometry;
3247 if (device->si_geometry == NULL)
3248 return B_NO_MEMORY;
3249 ObjectDeleter<device_geometry> geomDeleter(device->si_geometry);
3250
3251 // open read-only for now
3252 device->si_fd = open(deviceFile, O_RDONLY | O_NOCACHE);
3253 if (device->si_fd < 0) {
3254 if (errno == B_BUSY)
3255 INFORM("FAT driver does not permit multiple mount points at the same time\n");
3256 RETURN_ERROR(B_FROM_POSIX_ERROR(errno));
3257 }
3258
3259 // get device characteristics
3260 device_geometry* geometry = device->si_geometry;
3261 if (ioctl(device->si_fd, B_GET_GEOMETRY, geometry, sizeof(device_geometry)) == -1) {
3262 // support mounting disk images
3263 struct stat imageStat;
3264 if (fstat(device->si_fd, &imageStat) >= 0 && S_ISREG(imageStat.st_mode)) {
3265 uint8 bootSector[512];
3266 if (read_pos(device->si_fd, 0, bootSector, 512) != 512) {
3267 INFORM("bsd_device_init: bootsector read failure\n");
3268 close(device->si_fd);
3269 return B_ERROR;
3270 }
3271 geometry->bytes_per_sector = read16(bootSector, 0xb);
3272 geometry->sectors_per_track = 0;
3273 geometry->cylinder_count = 0;
3274 geometry->head_count = 0;
3275 geometry->removable = true;
3276 geometry->read_only = !(imageStat.st_mode & S_IWUSR);
3277 geometry->write_once = false;
3278 #ifndef FS_SHELL
3279 dev_t imageParentDev = dev_for_path(deviceFile);
3280 fs_info parentInfo;
3281 status_t status = fs_stat_dev(imageParentDev, &parentInfo);
3282 if (status != 0) {
3283 INFORM("bsd_device_init: fs_stat failure\n");
3284 close(device->si_fd);
3285 return B_FROM_POSIX_ERROR(status);
3286 }
3287 geometry->bytes_per_physical_sector = parentInfo.block_size;
3288 #endif
3289 device->si_mediasize = imageStat.st_size;
3290 } else {
3291 close(device->si_fd);
3292 RETURN_ERROR(B_FROM_POSIX_ERROR(errno));
3293 }
3294 } else {
3295 device->si_mediasize = 1ULL * geometry->head_count * geometry->cylinder_count
3296 * geometry->sectors_per_track * geometry->bytes_per_sector;
3297 }
3298
3299 if (geometry->read_only) {
3300 PRINT("%s is read-only\n", deviceFile);
3301 *_readOnly = true;
3302 }
3303
3304 if (*_readOnly == false && static_cast<uint64>(device->si_mediasize) >
3305 2ULL * 1000 * 1000 * 1000 * 1000) {
3306 // the driver has not been tested on volumes > 2 TB
3307 INFORM("The FAT driver does not currently support write access to volumes larger than 2 "
3308 "TB.\n");
3309 *_readOnly = true;
3310 }
3311
3312 if (geometry->bytes_per_sector != 0x200) {
3313 // FAT is compatible with 0x400, 0x800, and 0x1000 as well, but this driver has not
3314 // been tested with those values
3315 INFORM("The FAT driver does not currently support write access to volumes with > 1 block "
3316 "per sector\n");
3317 *_readOnly = true;
3318 }
3319
3320 if (*_readOnly == false) {
3321 // reopen it with read/write permissions
3322 close(device->si_fd);
3323 device->si_fd = open(deviceFile, O_RDWR | O_NOCACHE);
3324 if (device->si_fd < 0)
3325 RETURN_ERROR(B_FROM_POSIX_ERROR(errno));
3326 }
3327
3328 // Prevent multiple simultaneous mounts.
3329 #ifndef FS_SHELL
3330 status_t status = _kern_lock_node(device->si_fd);
3331 if (status != B_OK) {
3332 close(device->si_fd);
3333 RETURN_ERROR(status);
3334 }
3335 #endif
3336
3337 deviceDeleter.Detach();
3338 geomDeleter.Detach();
3339
3340 *bsdDevice = device;
3341
3342 return B_OK;
3343 }
3344
3345
3346 status_t
bsd_device_uninit(cdev * device)3347 bsd_device_uninit(cdev* device)
3348 {
3349 if (device == NULL)
3350 return B_OK;
3351
3352 if (device->si_fd >= 0) {
3353 if (close(device->si_fd) != 0)
3354 RETURN_ERROR(B_FROM_POSIX_ERROR(errno));
3355 } else {
3356 RETURN_ERROR(B_ERROR);
3357 }
3358
3359 delete device->si_geometry;
3360
3361 #ifndef FS_SHELL
3362 _kern_unlock_node(device->si_fd);
3363 #endif // FS_SHELL
3364
3365 delete device;
3366
3367 return B_OK;
3368 }
3369
3370
3371 /*! Create a FreeBSD-format vnode representing the device, to simulate a FreeBSD VFS environment
3372 for the ported driver code.
3373 */
3374 static status_t
dev_bsd_node_init(cdev * bsdDevice,vnode ** devNode)3375 dev_bsd_node_init(cdev* bsdDevice, vnode** devNode)
3376 {
3377 vnode* node;
3378 status_t status = B_FROM_POSIX_ERROR(getnewvnode(NULL, bsdDevice->si_mountpt, NULL, &node));
3379 if (status != B_OK)
3380 RETURN_ERROR(status);
3381
3382 // Set up the device node members that are accessed by the driver.
3383 // We don't give this node any private data.
3384 node->v_type = VBLK;
3385 node->v_rdev = bsdDevice;
3386 node->v_mount = NULL;
3387 SLIST_INIT(&node->v_bufobj.bo_clusterbufs);
3388 SLIST_INIT(&node->v_bufobj.bo_fatbufs);
3389 SLIST_INIT(&node->v_bufobj.bo_emptybufs);
3390 node->v_bufobj.bo_clusters = 0;
3391 node->v_bufobj.bo_fatblocks = 0;
3392 node->v_bufobj.bo_empties = 0;
3393 rw_lock_init(&node->v_bufobj.bo_lock.haikuRW, "FAT v_bufobj");
3394
3395 *devNode = node;
3396
3397 return B_OK;
3398 }
3399
3400
3401 status_t
dev_bsd_node_uninit(vnode * devNode)3402 dev_bsd_node_uninit(vnode* devNode)
3403 {
3404 if (devNode == NULL)
3405 return B_OK;
3406
3407 rw_lock_write_lock(&devNode->v_bufobj.bo_lock.haikuRW);
3408
3409 // free cluster-size struct bufs: first b_data, and then the bufs themselves
3410 buf* listEntry;
3411 SLIST_FOREACH(listEntry, &devNode->v_bufobj.bo_clusterbufs, link)
3412 {
3413 free(listEntry->b_data);
3414 }
3415 while (!SLIST_EMPTY(&devNode->v_bufobj.bo_clusterbufs)) {
3416 listEntry = SLIST_FIRST(&devNode->v_bufobj.bo_clusterbufs);
3417 SLIST_REMOVE_HEAD(&devNode->v_bufobj.bo_clusterbufs, link);
3418 free(listEntry);
3419 }
3420
3421 // free the FAT-block size bufs
3422 listEntry = NULL;
3423 SLIST_FOREACH(listEntry, &devNode->v_bufobj.bo_fatbufs, link)
3424 {
3425 free(listEntry->b_data);
3426 }
3427 while (!SLIST_EMPTY(&devNode->v_bufobj.bo_fatbufs)) {
3428 listEntry = SLIST_FIRST(&devNode->v_bufobj.bo_fatbufs);
3429 SLIST_REMOVE_HEAD(&devNode->v_bufobj.bo_fatbufs, link);
3430 free(listEntry);
3431 }
3432
3433 // free the bufs that were just used as pointers to the block cache
3434 while (!SLIST_EMPTY(&devNode->v_bufobj.bo_emptybufs)) {
3435 listEntry = SLIST_FIRST(&devNode->v_bufobj.bo_emptybufs);
3436 SLIST_REMOVE_HEAD(&devNode->v_bufobj.bo_emptybufs, link);
3437 free(listEntry);
3438 }
3439
3440 rw_lock_destroy(&devNode->v_bufobj.bo_lock.haikuRW);
3441
3442 free(devNode);
3443
3444 return B_OK;
3445 }
3446
3447
3448 /*! Further setup will be done later for mnt_data, mnt_stat.f_mntonname, mnt_volentry,
3449 and mnt_cache.
3450 */
3451 static status_t
bsd_volume_init(fs_volume * fsVolume,const uint32 flags,mount ** volume)3452 bsd_volume_init(fs_volume* fsVolume, const uint32 flags, mount** volume)
3453 {
3454 mount* bsdVolume = new(std::nothrow) mount;
3455 if (bsdVolume == NULL)
3456 return B_NO_MEMORY;
3457 ObjectDeleter<mount> volDeleter(bsdVolume);
3458
3459 bsdVolume->mnt_kern_flag = 0;
3460 bsdVolume->mnt_flag = 0;
3461 if ((flags & B_MOUNT_READ_ONLY) != 0)
3462 bsdVolume->mnt_flag |= MNT_RDONLY;
3463
3464 bsdVolume->mnt_vfc = new(std::nothrow) vfsconf;
3465 if ((bsdVolume)->mnt_vfc == NULL)
3466 return B_NO_MEMORY;
3467 bsdVolume->mnt_vfc->vfc_typenum = 1;
3468 // For the port, 1 is arbitrarily assigned as the type of this file system.
3469 // The member is accessed by the ported FreeBSD code but never used for anything.
3470
3471 bsdVolume->mnt_stat.f_iosize = FAT_IO_SIZE;
3472
3473 bsdVolume->mnt_data = NULL;
3474
3475 bsdVolume->mnt_iosize_max = FAT_IO_SIZE;
3476
3477 mutex_init(&bsdVolume->mnt_mtx.haikuMutex, "FAT volume");
3478
3479 bsdVolume->mnt_fsvolume = fsVolume;
3480
3481 bsdVolume->mnt_volentry = -1;
3482
3483 if (init_vcache(bsdVolume) != B_OK) {
3484 mutex_destroy(&(bsdVolume)->mnt_mtx.haikuMutex);
3485 delete bsdVolume->mnt_vfc;
3486 return B_ERROR;
3487 }
3488
3489 bsdVolume->mnt_cache = NULL;
3490
3491 *volume = bsdVolume;
3492
3493 volDeleter.Detach();
3494
3495 return B_OK;
3496 }
3497
3498
3499 status_t
bsd_volume_uninit(struct mount * volume)3500 bsd_volume_uninit(struct mount* volume)
3501 {
3502 if (volume == NULL)
3503 return B_OK;
3504
3505 delete volume->mnt_vfc;
3506
3507 mutex_destroy(&volume->mnt_mtx.haikuMutex);
3508
3509 uninit_vcache(volume);
3510
3511 delete volume;
3512
3513 return B_OK;
3514 }
3515
3516
3517 /*! Set up a msdosfsmount as mount::mnt_data and initialize the block cache.
3518
3519 */
3520 static status_t
fat_volume_init(vnode * devvp,mount * bsdVolume,const uint64_t fatFlags,const char * oemPref)3521 fat_volume_init(vnode* devvp, mount* bsdVolume, const uint64_t fatFlags, const char* oemPref)
3522 {
3523 // Read the boot sector of the filesystem, and then check the boot signature.
3524 // If not a dos boot sector then error out.
3525 cdev* dev = devvp->v_rdev;
3526
3527 uint8* bootsectorBuffer = static_cast<uint8*>(calloc(512, sizeof(char)));
3528 if (bootsectorBuffer == NULL)
3529 RETURN_ERROR(B_NO_MEMORY);
3530 MemoryDeleter bootsectorDeleter(bootsectorBuffer);
3531 if (read(dev->si_fd, bootsectorBuffer, 512) != 512)
3532 RETURN_ERROR(B_IO_ERROR);
3533
3534 enum FatType fatType;
3535 bool dos33;
3536 status_t status = check_bootsector(bootsectorBuffer, fatType, dos33);
3537 if (status != B_OK)
3538 RETURN_ERROR(status);
3539
3540 msdosfsmount* fatVolume = new(std::nothrow) msdosfsmount;
3541 if (fatVolume == NULL)
3542 RETURN_ERROR(B_NO_MEMORY);
3543 ObjectDeleter<msdosfsmount> volumeDeleter(fatVolume);
3544
3545 fatVolume->pm_cp = NULL;
3546 // Not implemented in port
3547
3548 fatVolume->pm_fsinfo = 0;
3549 fatVolume->pm_curfat = 0;
3550 fatVolume->pm_rootdirsize = 0;
3551 fatVolume->pm_fmod = 0;
3552
3553 fatVolume->pm_mountp = bsdVolume;
3554 fatVolume->pm_devvp = devvp;
3555 fatVolume->pm_odevvp = devvp;
3556 fatVolume->pm_bo = &devvp->v_bufobj;
3557 fatVolume->pm_dev = dev;
3558
3559 fatVolume->pm_flags = 0;
3560
3561 switch (fatType) {
3562 case fat12:
3563 fatVolume->pm_fatmask = FAT12_MASK;
3564 break;
3565 case fat16:
3566 fatVolume->pm_fatmask = FAT16_MASK;
3567 break;
3568 case fat32:
3569 fatVolume->pm_fatmask = FAT32_MASK;
3570 break;
3571 default:
3572 panic("invalid FAT type\n");
3573 }
3574
3575 fatVolume->pm_uid = geteuid();
3576 fatVolume->pm_gid = getegid();
3577
3578 fatVolume->pm_dirmask = S_IXUSR | S_IXGRP | S_IXOTH | S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR;
3579 fatVolume->pm_mask = S_IXUSR | S_IXGRP | S_IXOTH | S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR;
3580
3581 // populate those msdosfsmount members that are pulled directly from the BPB
3582 status = parse_bpb(fatVolume, reinterpret_cast<bootsector*>(bootsectorBuffer), dos33);
3583 if (status != B_OK)
3584 RETURN_ERROR(status);
3585
3586 fatVolume->pm_BlkPerSec = fatVolume->pm_BytesPerSec / DEV_BSIZE;
3587 if (static_cast<off_t>(fatVolume->pm_HugeSectors * fatVolume->pm_BlkPerSec) * DEV_BSIZE
3588 > dev->si_mediasize) {
3589 INFORM("sector count exceeds media size (%" B_PRIdOFF " > %" B_PRIdOFF ")\n",
3590 static_cast<off_t>(fatVolume->pm_HugeSectors) * fatVolume->pm_BlkPerSec * DEV_BSIZE,
3591 dev->si_mediasize);
3592 return B_BAD_VALUE;
3593 }
3594 uint8 SecPerClust = fatVolume->pm_bpb.bpbSecPerClust;
3595
3596 // like FreeBSD, the port uses 512-byte blocks as the primary unit of disk data
3597 // rather then the device-dependent sector size
3598 fatVolume->pm_fsinfo *= fatVolume->pm_BlkPerSec;
3599 if (static_cast<uint64>(fatVolume->pm_HugeSectors) * fatVolume->pm_BlkPerSec > UINT_MAX) {
3600 INFORM("pm_HugeSectors overflows when converting from sectors to 512-byte blocks\n");
3601 return B_ERROR;
3602 }
3603 fatVolume->pm_HugeSectors *= fatVolume->pm_BlkPerSec;
3604 fatVolume->pm_HiddenSects *= fatVolume->pm_BlkPerSec;
3605 fatVolume->pm_FATsecs *= fatVolume->pm_BlkPerSec;
3606 SecPerClust *= fatVolume->pm_BlkPerSec;
3607
3608 fatVolume->pm_fatblk = fatVolume->pm_ResSectors * fatVolume->pm_BlkPerSec;
3609
3610 if (FAT32(fatVolume) == true) {
3611 fatVolume->pm_fatmult = 4;
3612 fatVolume->pm_fatdiv = 1;
3613 fatVolume->pm_firstcluster
3614 = fatVolume->pm_fatblk + fatVolume->pm_FATs * fatVolume->pm_FATsecs;
3615 } else {
3616 fatVolume->pm_curfat = 0;
3617 fatVolume->pm_rootdirblk
3618 = fatVolume->pm_fatblk + fatVolume->pm_FATs * fatVolume->pm_FATsecs;
3619 fatVolume->pm_rootdirsize = howmany(fatVolume->pm_RootDirEnts * sizeof(direntry),
3620 DEV_BSIZE); // in blocks
3621 fatVolume->pm_firstcluster = fatVolume->pm_rootdirblk + fatVolume->pm_rootdirsize;
3622 }
3623
3624 if (fatVolume->pm_HugeSectors <= fatVolume->pm_firstcluster)
3625 RETURN_ERROR(B_BAD_VALUE);
3626
3627 fatVolume->pm_maxcluster
3628 = (fatVolume->pm_HugeSectors - fatVolume->pm_firstcluster) / SecPerClust + 1;
3629
3630 if (FAT32(fatVolume) == false) {
3631 if (fatVolume->pm_maxcluster <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) {
3632 // This will usually be a floppy disk. This size makes sure that one FAT entry will
3633 // not be split across multiple blocks.
3634 fatVolume->pm_fatmult = 3;
3635 fatVolume->pm_fatdiv = 2;
3636 } else {
3637 fatVolume->pm_fatmult = 2;
3638 fatVolume->pm_fatdiv = 1;
3639 }
3640 }
3641
3642 fatVolume->pm_fatsize = fatVolume->pm_FATsecs * DEV_BSIZE;
3643
3644 uint32 fatCapacity = (fatVolume->pm_fatsize / fatVolume->pm_fatmult) * fatVolume->pm_fatdiv;
3645 if (fatVolume->pm_maxcluster >= fatCapacity) {
3646 INFORM("number of clusters (%ld) exceeds FAT capacity (%" B_PRIu32 ") "
3647 "(some clusters are inaccessible)\n", fatVolume->pm_maxcluster + 1, fatCapacity);
3648 fatVolume->pm_maxcluster = fatCapacity - 1;
3649 }
3650
3651 if (FAT12(fatVolume) != 0)
3652 fatVolume->pm_fatblocksize = 3 * 512;
3653 else
3654 fatVolume->pm_fatblocksize = DEV_BSIZE;
3655 fatVolume->pm_fatblocksec = fatVolume->pm_fatblocksize / DEV_BSIZE;
3656 fatVolume->pm_bnshift = ffs(DEV_BSIZE) - 1;
3657
3658 // compute mask and shift value for isolating cluster relative byte offsets and cluster
3659 // numbers from a file offset
3660 fatVolume->pm_bpcluster = SecPerClust * DEV_BSIZE;
3661 fatVolume->pm_crbomask = fatVolume->pm_bpcluster - 1;
3662 fatVolume->pm_cnshift = ffs(fatVolume->pm_bpcluster) - 1;
3663
3664 // this will be updated later if fsinfo exists
3665 fatVolume->pm_nxtfree = 3;
3666
3667 // check for valid cluster size - must be a power of 2
3668 if ((fatVolume->pm_bpcluster ^ (1 << fatVolume->pm_cnshift)) != 0)
3669 RETURN_ERROR(B_BAD_VALUE);
3670
3671 status = check_fat(fatVolume);
3672 if (status != B_OK)
3673 RETURN_ERROR(status);
3674
3675 bool readOnly = (fatFlags & MSDOSFSMNT_RONLY) != 0;
3676
3677 // check that the partition is large enough to contain the file system
3678 if (dev->si_geometry == NULL) {
3679 INFORM("si_geometry not initialized\n");
3680 return B_ERROR;
3681 }
3682 uint32 fsSectors = fatVolume->pm_HugeSectors / fatVolume->pm_BlkPerSec;
3683 // convert back from 512-byte blocks to sectors
3684 if (fsSectors > dev->si_mediasize / dev->si_geometry->bytes_per_sector) {
3685 INFORM("dosfs: volume extends past end of partition, mounting read-only\n");
3686 readOnly = true;
3687 }
3688
3689 // check for sector count overflow in the BPB, which is only possible for FAT32 volumes
3690 if (FAT32(fatVolume) != 0) {
3691 // given the size of the FAT table, how many sectors do we expect to have in the volume?
3692 uint32 fatSectors = fatVolume->pm_FATsecs / fatVolume->pm_BlkPerSec;
3693 // convert back from 512-byte blocks to sectors
3694 uint32 minUsedFatSectors = fatSectors - 8 - (SECTORS_PER_CLUSTER(fatVolume) - 1);
3695 // The math recommended by Microsoft to estimate required FAT sectors at initialization
3696 // may overestimate by up to 8 sectors; fsck.fat also aligns the FAT to cluster size
3697 uint32 fatEntriesPerSector = fatVolume->pm_BytesPerSec / 4;
3698 uint32 minFatEntries = minUsedFatSectors * fatEntriesPerSector - (fatEntriesPerSector - 1);
3699 // the last utilized sector of a FAT contains at least one entry
3700 uint64 minDataSectors = (minFatEntries - 2) * SECTORS_PER_CLUSTER(fatVolume);
3701 // 2 reserved cluster numbers
3702 uint64 minTotalSectors = fatVolume->pm_ResSectors +
3703 fatVolume->pm_FATsecs * fatVolume->pm_FATs + minDataSectors;
3704 if (fsSectors < minTotalSectors) {
3705 INFORM("possible sector count overflow (%" B_PRIu32 " vs. %" B_PRIu64 "), mounting "
3706 "read-only\n", fsSectors, minTotalSectors);
3707 readOnly = true;
3708 }
3709 }
3710
3711 status = read_label(fatVolume, dev->si_fd, bootsectorBuffer, dev->si_name);
3712 if (status != B_OK)
3713 RETURN_ERROR(status);
3714
3715 // Set up the block cache.
3716 // If the cached block size is ever changed, functions that work with the block cache
3717 // will need to be re-examined because they assume a size of 512 bytes
3718 // (e.g. dosfs_fsync, read_fsinfo, write_fsinfo, sync_clusters, discard_clusters,
3719 // dosfs_write_fs_stat, and the functions defined in vfs_bio.c).
3720 bsdVolume->mnt_cache
3721 = block_cache_create(dev->si_fd, fatVolume->pm_HugeSectors, CACHED_BLOCK_SIZE, readOnly);
3722 if (bsdVolume->mnt_cache == NULL)
3723 return B_ERROR;
3724
3725 status = read_fsinfo(fatVolume, devvp);
3726 if (status != B_OK) {
3727 block_cache_delete(bsdVolume->mnt_cache, false);
3728 return status;
3729 }
3730
3731 bsdVolume->mnt_data = fatVolume;
3732
3733 // allocate memory for the bitmap of allocated clusters, and then fill it in
3734 fatVolume->pm_inusemap = reinterpret_cast<u_int*>(malloc(
3735 howmany(fatVolume->pm_maxcluster + 1, N_INUSEBITS) * sizeof(*fatVolume->pm_inusemap)));
3736 if (fatVolume->pm_inusemap == NULL) {
3737 block_cache_delete(bsdVolume->mnt_cache, false);
3738 bsdVolume->mnt_data = NULL;
3739 RETURN_ERROR(B_NO_MEMORY);
3740 }
3741 MemoryDeleter inusemapDeleter(fatVolume->pm_inusemap);
3742
3743 rw_lock_init(&fatVolume->pm_fatlock.haikuRW, "fatlock");
3744
3745 // update this flag now so it will be applied in getblkx,
3746 // but wait to set the fatVolume flags; fillinusemap is designed to run before they are set
3747 if (readOnly == true)
3748 bsdVolume->mnt_flag |= MNT_RDONLY;
3749
3750 // attempt to read the FAT into memory in advance of fillinusemap, to prevent fillinusemap
3751 // from doing a separate disk read for each block
3752 if (fatVolume->pm_FATsecs > 4) {
3753 size_t fatBlocks = fatVolume->pm_FATsecs;
3754 block_cache_prefetch(bsdVolume->mnt_cache, static_cast<off_t>(fatVolume->pm_fatblk),
3755 &fatBlocks);
3756 }
3757
3758 // have the inuse map filled in
3759 rw_lock_write_lock(&fatVolume->pm_fatlock.haikuRW);
3760 status = B_FROM_POSIX_ERROR(fillinusemap(fatVolume));
3761 rw_lock_write_unlock(&fatVolume->pm_fatlock.haikuRW);
3762 if (status != 0) {
3763 rw_lock_destroy(&fatVolume->pm_fatlock.haikuRW);
3764 block_cache_delete(bsdVolume->mnt_cache, false);
3765 bsdVolume->mnt_data = NULL;
3766 RETURN_ERROR(status);
3767 }
3768
3769 // some flags from the FreeBSD driver are not supported in the port
3770 ASSERT((fatVolume->pm_flags
3771 & (MSDOSFSMNT_SHORTNAME | MSDOSFSMNT_LONGNAME | MSDOSFSMNT_NOWIN95 | MSDOSFS_ERR_RO))
3772 == 0);
3773 fatVolume->pm_flags |= fatFlags;
3774
3775 if (readOnly == true) {
3776 fatVolume->pm_flags |= MSDOSFSMNT_RONLY;
3777 } else {
3778 status = B_FROM_POSIX_ERROR(markvoldirty(fatVolume, 1));
3779 if (status != B_OK) {
3780 rw_lock_destroy(&fatVolume->pm_fatlock.haikuRW);
3781 block_cache_delete(bsdVolume->mnt_cache, false);
3782 bsdVolume->mnt_data = NULL;
3783 RETURN_ERROR(status);
3784 }
3785 fatVolume->pm_fmod = 1;
3786 }
3787
3788 status = iconv_init(fatVolume, oemPref);
3789 if (status != B_OK) {
3790 rw_lock_destroy(&fatVolume->pm_fatlock.haikuRW);
3791 block_cache_delete(bsdVolume->mnt_cache, false);
3792 bsdVolume->mnt_data = NULL;
3793 RETURN_ERROR(status);
3794 }
3795
3796 rw_lock_init(&fatVolume->pm_checkpath_lock.haikuRW, "fat cp");
3797
3798 volumeDeleter.Detach();
3799 inusemapDeleter.Detach();
3800
3801 return B_OK;
3802 }
3803
3804
3805 /*! Clean up the msdosfsmount and the block cache.
3806 @pre pm_devvp and pm_dev still exist.
3807 */
3808 status_t
fat_volume_uninit(msdosfsmount * volume)3809 fat_volume_uninit(msdosfsmount* volume)
3810 {
3811 if (volume == NULL)
3812 return B_OK;
3813
3814 status_t status = B_OK;
3815 if (((volume)->pm_flags & MSDOSFSMNT_RONLY) == 0) {
3816 rw_lock_write_lock(&volume->pm_fatlock.haikuRW);
3817 status = B_FROM_POSIX_ERROR(markvoldirty((volume), 0));
3818 rw_lock_write_unlock(&volume->pm_fatlock.haikuRW);
3819 if (status != B_OK) {
3820 markvoldirty((volume), 1);
3821 REPORT_ERROR(status);
3822 }
3823 }
3824
3825 if ((volume->pm_flags & MSDOSFSMNT_KICONV) != 0 && msdosfs_iconv != NULL) {
3826 if (volume->pm_w2u != NULL)
3827 msdosfs_iconv->close(volume->pm_w2u);
3828 if (volume->pm_u2w != NULL)
3829 msdosfs_iconv->close(volume->pm_u2w);
3830 if (volume->pm_d2u != NULL)
3831 msdosfs_iconv->close(volume->pm_d2u);
3832 if (volume->pm_u2d != NULL)
3833 msdosfs_iconv->close(volume->pm_u2d);
3834 delete msdosfs_iconv;
3835 msdosfs_iconv = NULL;
3836 }
3837
3838 status = write_fsinfo(volume);
3839 if (status != B_OK)
3840 REPORT_ERROR(status);
3841
3842 if (volume->pm_mountp->mnt_cache != NULL) {
3843 block_cache_delete(volume->pm_mountp->mnt_cache,
3844 (volume->pm_flags & MSDOSFSMNT_RONLY) == 0);
3845 volume->pm_mountp->mnt_cache = NULL;
3846 }
3847
3848 free(volume->pm_inusemap);
3849
3850 rw_lock_destroy(&volume->pm_fatlock.haikuRW);
3851 rw_lock_destroy(&volume->pm_checkpath_lock.haikuRW);
3852
3853 delete volume;
3854
3855 return status;
3856 }
3857
3858
3859 static status_t
iterative_io_get_vecs_hook(void * cookie,io_request * request,off_t offset,size_t size,struct file_io_vec * vecs,size_t * _count)3860 iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset, size_t size,
3861 struct file_io_vec* vecs, size_t* _count)
3862 {
3863 vnode* bsdNode = reinterpret_cast<vnode*>(cookie);
3864 msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdNode->v_mount->mnt_data);
3865
3866 return file_map_translate(bsdNode->v_file_map, offset, size, vecs, _count,
3867 fatVolume->pm_bpcluster);
3868 }
3869
3870
3871 static status_t
iterative_io_finished_hook(void * cookie,io_request * request,status_t status,bool partialTransfer,size_t bytesTransferred)3872 iterative_io_finished_hook(void* cookie, io_request* request, status_t status, bool partialTransfer,
3873 size_t bytesTransferred)
3874 {
3875 vnode* bsdNode = reinterpret_cast<vnode*>(cookie);
3876 denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
3877
3878 rw_lock_read_unlock(&bsdNode->v_vnlock->haikuRW);
3879 put_vnode(bsdNode->v_mount->mnt_fsvolume, fatNode->de_inode);
3880
3881 return B_OK;
3882 }
3883
3884
3885 static uint32
dosfs_get_supported_operations(partition_data * partition,uint32 mask)3886 dosfs_get_supported_operations(partition_data* partition, uint32 mask)
3887 {
3888 FUNCTION();
3889
3890 return B_DISK_SYSTEM_SUPPORTS_INITIALIZING | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
3891 | B_DISK_SYSTEM_SUPPORTS_WRITING;
3892 }
3893
3894
3895 static status_t
dos_std_ops(int32 op,...)3896 dos_std_ops(int32 op, ...)
3897 {
3898 switch (op) {
3899 case B_MODULE_INIT:
3900 FUNCTION_START("B_MODULE_INIT\n");
3901 #ifdef _KERNEL_MODE
3902 add_debugger_command("fat", kprintf_volume, "dump a FAT private volume");
3903 add_debugger_command("fat_node", kprintf_node, "dump a FAT private node");
3904 #endif // _KERNEL_MODE
3905 break;
3906
3907 case B_MODULE_UNINIT:
3908 FUNCTION_START("B_MODULE_UNINIT\n");
3909 #ifdef _KERNEL_MODE
3910 remove_debugger_command("fat", kprintf_volume);
3911 remove_debugger_command("fat_node", kprintf_node);
3912 #endif // _KERNEL_MODE
3913 break;
3914
3915 default:
3916 return B_ERROR;
3917 }
3918
3919 return B_OK;
3920 }
3921
3922
3923 fs_volume_ops gFATVolumeOps = {
3924 &dosfs_unmount,
3925 &dosfs_read_fs_stat,
3926 &dosfs_write_fs_stat,
3927 &dosfs_sync,
3928 &dosfs_read_vnode,
3929
3930 // index directory & index operations
3931 NULL, //&fs_open_index_dir,
3932 NULL, //&fs_close_index_dir,
3933 NULL, //&fs_free_index_dir_cookie,
3934 NULL, //&fs_read_index_dir,
3935 NULL, //&fs_rewind_index_dir,
3936
3937 NULL, //&fs_create_index,
3938 NULL, //&fs_remove_index,
3939 NULL, //&fs_stat_index,
3940
3941 // query operations
3942 NULL, //&fs_open_query,
3943 NULL, //&fs_close_query,
3944 NULL, //&fs_free_query_cookie,
3945 NULL, //&fs_read_query,
3946 NULL, //&fs_rewind_query,
3947 };
3948
3949
3950 fs_vnode_ops gFATVnodeOps = {
3951 // vnode operations
3952 &dosfs_walk,
3953 NULL, // fs_get_vnode_name,
3954 &dosfs_release_vnode,
3955 &dosfs_remove_vnode,
3956
3957 // VM file access
3958 &dosfs_can_page,
3959 &dosfs_read_pages,
3960 &dosfs_write_pages,
3961
3962 &dosfs_io,
3963 NULL, // cancel_io()
3964
3965 &dosfs_get_file_map,
3966
3967 NULL, // fs_ioctl()
3968 NULL, // fs_set_flags,
3969 NULL, // fs_select
3970 NULL, // fs_deselect
3971 &dosfs_fsync,
3972
3973 NULL, // fs_read_symlink,
3974 NULL, // fs_create_symlink,
3975
3976 &dosfs_link,
3977 &dosfs_unlink,
3978 &dosfs_rename,
3979
3980 &dosfs_access,
3981 &dosfs_rstat,
3982 &dosfs_wstat,
3983 NULL, // fs_preallocate,
3984
3985 // file operations
3986 &dosfs_create,
3987 &dosfs_open,
3988 &dosfs_close,
3989 &dosfs_free_cookie,
3990 &dosfs_read,
3991 &dosfs_write,
3992
3993 // directory operations
3994 &dosfs_mkdir,
3995 &dosfs_rmdir,
3996 &dosfs_opendir,
3997 &dosfs_closedir,
3998 &dosfs_free_dircookie,
3999 &dosfs_readdir,
4000 &dosfs_rewinddir,
4001
4002 // attribute directory operations
4003 &dosfs_open_attrdir,
4004 &dosfs_close_attrdir,
4005 &dosfs_free_attrdir_cookie,
4006 &dosfs_read_attrdir,
4007 &dosfs_rewind_attrdir,
4008
4009 // attribute operations
4010 &dosfs_create_attr,
4011 &dosfs_open_attr,
4012 &dosfs_close_attr,
4013 &dosfs_free_attr_cookie,
4014 &dosfs_read_attr,
4015 &dosfs_write_attr,
4016
4017 &dosfs_read_attr_stat,
4018 NULL, // fs_write_attr_stat,
4019 NULL, // fs_rename_attr,
4020 NULL, // fs_remove_attr
4021 };
4022
4023
4024 static file_system_module_info sFATBSDFileSystem = {
4025 {
4026 "file_systems/fat" B_CURRENT_FS_API_VERSION,
4027 0,
4028 dos_std_ops,
4029 },
4030 "fat", // short_name
4031 "FAT32 File System", // pretty_name
4032
4033 // DDM flags
4034 0
4035 // | B_DISK_SYSTEM_SUPPORTS_CHECKING
4036 // | B_DISK_SYSTEM_SUPPORTS_REPAIRING
4037 // | B_DISK_SYSTEM_SUPPORTS_RESIZING
4038 // | B_DISK_SYSTEM_SUPPORTS_MOVING
4039 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME
4040 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS
4041 | B_DISK_SYSTEM_SUPPORTS_INITIALIZING
4042 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
4043 // | B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING
4044 // | B_DISK_SYSTEM_SUPPORTS_DEFRAGMENTING_WHILE_MOUNTED
4045 // | B_DISK_SYSTEM_SUPPORTS_CHECKING_WHILE_MOUNTED
4046 // | B_DISK_SYSTEM_SUPPORTS_REPAIRING_WHILE_MOUNTED
4047 // | B_DISK_SYSTEM_SUPPORTS_RESIZING_WHILE_MOUNTED
4048 // | B_DISK_SYSTEM_SUPPORTS_MOVING_WHILE_MOUNTED
4049 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME_WHILE_MOUNTED
4050 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS_WHILE_MOUNTED
4051 | B_DISK_SYSTEM_SUPPORTS_WRITING,
4052
4053 // scanning
4054 &dosfs_identify_partition,
4055 &dosfs_scan_partition,
4056 &dosfs_free_identify_partition_cookie,
4057 NULL, // free_partition_content_cookie
4058
4059 &dosfs_mount,
4060
4061 // capability querying operations
4062 &dosfs_get_supported_operations,
4063
4064 NULL, // validate_resize
4065 NULL, // validate_move
4066 NULL, // validate_set_content_name
4067 NULL, // validate_set_content_parameters
4068 NULL, // validate_initialize
4069
4070 // shadow partition modification
4071 NULL, // shadow_changed
4072
4073 // writing
4074 NULL, // defragment
4075 NULL, // repair
4076 NULL, // resize
4077 NULL, // move
4078 NULL, // set_content_name
4079 NULL, // set_content_parameters
4080 dosfs_initialize,
4081 dosfs_uninitialize};
4082
4083
4084 module_info* modules[] = {
4085 reinterpret_cast<module_info*>(&sFATBSDFileSystem),
4086 NULL,
4087 };
4088