1/*
2	Copyright 1999-2001, Be Incorporated.   All Rights Reserved.
3	This file may be used under the terms of the Be Sample Code License.
4*/
5
6#include <stdlib.h>
7#include <string.h>
8#include <sys/stat.h>
9
10#include <fs_cache.h>
11#include <fs_info.h>
12#include <Drivers.h>
13#include <KernelExport.h>
14#include <NodeMonitor.h>
15
16#include <time.h>
17
18#include "iter.h"
19#include "dosfs.h"
20#include "dlist.h"
21#include "fat.h"
22#include "dir.h"
23#include "file.h"
24#include "attr.h"
25#include "vcache.h"
26#include "util.h"
27
28#define DPRINTF(a,b) if (debug_file > (a)) dprintf b
29
30#define MAX_FILE_SIZE 0xffffffffLL
31
32
33typedef struct filecookie {
34	uint32		mode;		// open mode
35} filecookie;
36
37
38mode_t
39make_mode(nspace *volume, vnode *node)
40{
41	mode_t result = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH;
42	if (node->mode & FAT_SUBDIR) {
43		result &= ~S_IFREG;
44		result |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
45	}
46	if ((node->mode & FAT_READ_ONLY) != 0)
47		result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
48
49	return result;
50}
51
52
53status_t
54dosfs_get_vnode_name(fs_volume *_ns, fs_vnode *_node, char *buffer,
55	size_t bufferSize)
56{
57	vnode   *node = (vnode*)_node->private_node;
58	strlcpy(buffer, node->filename, bufferSize);
59	return B_OK;
60}
61
62
63status_t write_vnode_entry(nspace *vol, vnode *node)
64{
65	uint32 i;
66	struct diri diri;
67	uint8 *buffer;
68
69
70	// TODO : is it needed ? vfs job ?
71	// don't update entries of deleted files
72	//if (is_vnode_removed(vol->id, node->vnid) > 0) return 0;
73
74	// XXX: should check if directory position is still valid even
75	// though we do the is_vnode_removed check above
76
77	if ((node->cluster != 0) && !IS_DATA_CLUSTER(node->cluster)) {
78		dprintf("write_vnode_entry called on invalid cluster (%" B_PRIu32 ")\n",
79			node->cluster);
80		return EINVAL;
81	}
82
83	buffer = diri_init(vol, VNODE_PARENT_DIR_CLUSTER(node), node->eindex, &diri);
84	if (buffer == NULL)
85		return ENOENT;
86
87	diri_make_writable(&diri);
88	buffer[0x0b] = node->mode; // file attributes
89
90	memset(buffer+0xc, 0, 0x16-0xc);
91	i = time_t2dos(node->st_crtim);
92	buffer[0x0e] = i & 0xff;
93	buffer[0x0f] = (i >> 8) & 0xff;
94	buffer[0x10] = (i >> 16) & 0xff;
95	buffer[0x11] = (i >> 24) & 0xff;
96	i = time_t2dos(node->st_time);
97	buffer[0x16] = i & 0xff;
98	buffer[0x17] = (i >> 8) & 0xff;
99	buffer[0x18] = (i >> 16) & 0xff;
100	buffer[0x19] = (i >> 24) & 0xff;
101	buffer[0x1a] = node->cluster & 0xff;	// starting cluster
102	buffer[0x1b] = (node->cluster >> 8) & 0xff;
103	if (vol->fat_bits == 32) {
104		buffer[0x14] = (node->cluster >> 16) & 0xff;
105		buffer[0x15] = (node->cluster >> 24) & 0xff;
106	}
107	if (node->mode & FAT_SUBDIR) {
108		buffer[0x1c] = buffer[0x1d] = buffer[0x1e] = buffer[0x1f] = 0;
109	} else {
110		buffer[0x1c] = node->st_size & 0xff;	// file size
111		buffer[0x1d] = (node->st_size >> 8) & 0xff;
112		buffer[0x1e] = (node->st_size >> 16) & 0xff;
113		buffer[0x1f] = (node->st_size >> 24) & 0xff;
114	}
115
116	diri_free(&diri);
117
118	// TODO: figure out which stats have actually changed
119	notify_stat_changed(vol->id, -1, node->vnid, B_STAT_MODE | B_STAT_UID
120		| B_STAT_GID | B_STAT_SIZE | B_STAT_ACCESS_TIME
121		| B_STAT_MODIFICATION_TIME | B_STAT_CREATION_TIME
122		| B_STAT_CHANGE_TIME);
123
124	return B_OK;
125}
126
127
128// called when fs is done with vnode
129// after close, etc. free vnode resources here
130status_t
131dosfs_release_vnode(fs_volume *_vol, fs_vnode *_node, bool reenter)
132{
133	nspace *vol = (nspace *)_vol->private_volume;
134	vnode *node = (vnode *)_node->private_node;
135
136	TOUCH(reenter);
137
138	if (node != NULL) {
139		DPRINTF(0, ("dosfs_release_vnode (ino_t %" B_PRIdINO ")\n", node->vnid));
140
141		if ((vol->fs_flags & FS_FLAGS_OP_SYNC) && node->dirty) {
142			LOCK_VOL(vol);
143			_dosfs_sync(vol);
144			UNLOCK_VOL(vol);
145		}
146
147#if TRACK_FILENAME
148		if (node->filename) free(node->filename);
149#endif
150
151		if (node->vnid != vol->root_vnode.vnid) {
152			file_cache_delete(node->cache);
153			file_map_delete(node->file_map);
154			free(node);
155		}
156	}
157
158	return 0;
159}
160
161
162status_t
163dosfs_rstat(fs_volume *_vol, fs_vnode *_node, struct stat *st)
164{
165	nspace	*vol = (nspace*)_vol->private_volume;
166	vnode	*node = (vnode*)_node->private_node;
167
168	LOCK_VOL(vol);
169
170	DPRINTF(1, ("dosfs_rstat (vnode id %" B_PRIdINO ")\n", node->vnid));
171
172	st->st_dev = vol->id;
173	st->st_ino = node->vnid;
174	st->st_mode = make_mode(vol, node);
175
176	st->st_nlink = 1;
177	st->st_uid = 0;
178	st->st_gid = 0;
179	st->st_size = node->st_size;
180	st->st_blocks = (node->st_size + 511) / 512;
181	st->st_blksize = 0x10000; /* this value was chosen arbitrarily */
182	st->st_atim.tv_sec = st->st_mtim.tv_sec = st->st_ctim.tv_sec
183		= node->st_time;
184	st->st_crtim.tv_sec = node->st_crtim;
185	st->st_atim.tv_nsec = st->st_mtim.tv_nsec = st->st_ctim.tv_nsec
186		= st->st_crtim.tv_nsec = 0;
187
188	UNLOCK_VOL(vol);
189
190	return B_NO_ERROR;
191}
192
193
194status_t
195dosfs_wstat(fs_volume *_vol, fs_vnode *_node, const struct stat *st,
196	uint32 mask)
197{
198	int err = B_OK;
199	nspace	*vol = (nspace*)_vol->private_volume;
200	vnode	*node = (vnode*)_node->private_node;
201	bool dirty = false;
202
203	LOCK_VOL(vol);
204
205	DPRINTF(0, ("dosfs_wstat (vnode id %" B_PRIdINO ")\n", node->vnid));
206
207	if (vol->flags & B_FS_IS_READONLY) {
208		dprintf("can't wstat on read-only volume\n");
209		UNLOCK_VOL(vol);
210		return EROFS;
211	}
212
213	if (node->disk_image == 2) {
214		dprintf("can't wstat disk image\n");
215		UNLOCK_VOL(vol);
216		return EPERM;
217	}
218
219	if ((mask & B_STAT_MODE) != 0) {
220		DPRINTF(0, ("setting file mode to %o\n", st->st_mode));
221		if (st->st_mode & S_IWUSR)
222			node->mode &= ~FAT_READ_ONLY;
223		else
224			node->mode |= FAT_READ_ONLY;
225		dirty = true;
226	}
227
228	if ((mask & B_STAT_SIZE) != 0) {
229		DPRINTF(0, ("setting file size to %" B_PRIdOFF "\n", st->st_size));
230		if (node->mode & FAT_SUBDIR) {
231			dprintf("dosfs_wstat: can't set file size of directory!\n");
232			err = EISDIR;
233		} else if (st->st_size > MAX_FILE_SIZE) {
234			dprintf("dosfs_wstat: desired file size exceeds fat limit\n");
235			err = E2BIG;
236		} else {
237			uint32 clusters = (st->st_size + vol->bytes_per_sector
238					* vol->sectors_per_cluster - 1) / vol->bytes_per_sector
239				/ vol->sectors_per_cluster;
240			DPRINTF(0, ("setting fat chain length to %" B_PRIu32 " clusters\n",
241				clusters));
242			if ((err = set_fat_chain_length(vol, node, clusters, false))
243					== B_OK) {
244				node->st_size = st->st_size;
245				node->iteration++;
246				dirty = true;
247				file_cache_set_size(node->cache, node->st_size);
248				file_map_set_size(node->file_map, node->st_size);
249			}
250		}
251	}
252
253	if ((mask & B_STAT_MODIFICATION_TIME) != 0) {
254		DPRINTF(0, ("setting modification time\n"));
255		if ((node->mode & FAT_SUBDIR) == 0)
256			node->mode |= FAT_ARCHIVE;
257		node->st_time = st->st_mtime;
258		dirty = true;
259	}
260
261	if ((mask & B_STAT_CREATION_TIME) != 0) {
262		DPRINTF(0, ("setting creation time\n"));
263		// As a file's modification time is also set when it is created,
264		// the archive bit should be set automatically.
265		node->st_crtim = st->st_crtime;
266		dirty = true;
267	}
268
269	if (dirty) {
270		write_vnode_entry(vol, node);
271
272		if (vol->fs_flags & FS_FLAGS_OP_SYNC) {
273			// sync the filesystem
274			_dosfs_sync(vol);
275			node->dirty = false;
276		}
277	}
278
279	if (err != B_OK) DPRINTF(0, ("dosfs_wstat (%s)\n", strerror(err)));
280
281	UNLOCK_VOL(vol);
282
283	return err;
284}
285
286
287status_t
288dosfs_open(fs_volume *_vol, fs_vnode *_node, int omode, void **_cookie)
289{
290	status_t	result = EINVAL;
291	nspace *vol = (nspace *)_vol->private_volume;
292	vnode* 	node = (vnode*)_node->private_node;
293	filecookie *cookie;
294
295	*_cookie = NULL;
296
297	LOCK_VOL(vol);
298
299	DPRINTF(0, ("dosfs_open: vnode id %" B_PRIdINO ", omode %o\n", node->vnid,
300		omode));
301
302	if (omode & O_CREAT) {
303		dprintf("dosfs_open called with O_CREAT. call dosfs_create instead!\n");
304		result = EINVAL;
305		goto error;
306	}
307
308	if ((vol->flags & B_FS_IS_READONLY) ||
309		(node->mode & FAT_READ_ONLY) ||
310		(node->disk_image != 0) ||
311		// allow opening directories for ioctl() calls
312		// and to let BVolume to work
313		(node->mode & FAT_SUBDIR)) {
314		omode = (omode & ~O_RWMASK) | O_RDONLY;
315	}
316
317	if ((omode & O_TRUNC) && ((omode & O_RWMASK) == O_RDONLY)) {
318		DPRINTF(0, ("can't open file for reading with O_TRUNC\n"));
319		result = EPERM;
320		goto error;
321	}
322
323	if (omode & O_TRUNC) {
324		DPRINTF(0, ("dosfs_open called with O_TRUNC set\n"));
325		if ((result = set_fat_chain_length(vol, node, 0, false)) != B_OK) {
326			dprintf("dosfs_open: error truncating file\n");
327			goto error;
328		}
329		node->mode = 0;
330		node->st_size = 0;
331		node->iteration++;
332	}
333
334	if ((cookie = calloc(sizeof(filecookie), 1)) == NULL) {
335		result = ENOMEM;
336		goto error;
337	}
338
339	cookie->mode = omode;
340	*_cookie = cookie;
341	result = B_OK;
342
343error:
344	if (result != B_OK) DPRINTF(0, ("dosfs_open (%s)\n", strerror(result)));
345
346	UNLOCK_VOL(vol);
347	return result;
348}
349
350
351status_t
352dosfs_read(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t pos,
353			void *buf, size_t *len)
354{
355	nspace	*vol = (nspace *)_vol->private_volume;
356	vnode	*node = (vnode *)_node->private_node;
357	filecookie *cookie = (filecookie *)_cookie;
358	int result = B_OK;
359
360	LOCK_VOL(vol);
361
362	if (node->mode & FAT_SUBDIR) {
363		DPRINTF(0, ("dosfs_read called on subdirectory %" B_PRIdINO "\n",
364			node->vnid));
365		*len = 0;
366		UNLOCK_VOL(vol);
367		return EISDIR;
368	}
369
370	DPRINTF(0, ("dosfs_read called %" B_PRIuSIZE " bytes at %" B_PRIdOFF
371		" (vnode id %" B_PRIdINO ")\n", *len, pos, node->vnid));
372
373	result = file_cache_read(node->cache, cookie, pos, buf, len);
374
375	if (result != B_OK) {
376		DPRINTF(0, ("dosfs_read (%s)\n", strerror(result)));
377	} else {
378		DPRINTF(0, ("dosfs_read: read %" B_PRIuSIZE " bytes\n", *len));
379	}
380	UNLOCK_VOL(vol);
381
382	return result;
383}
384
385
386status_t
387dosfs_write(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t pos,
388	const void *buf, size_t *len)
389{
390	nspace	*vol = (nspace *)_vol->private_volume;
391	vnode	*node = (vnode *)_node->private_node;
392	filecookie *cookie = (filecookie *)_cookie;
393	int result = B_OK;
394
395	LOCK_VOL(vol);
396
397	if ((vol->flags & B_FS_IS_READONLY) != 0) {
398		UNLOCK_VOL(vol);
399		return EROFS;
400	}
401
402	if (node->mode & FAT_SUBDIR) {
403		DPRINTF(0, ("dosfs_write called on subdirectory %" B_PRIdINO "\n",
404			node->vnid));
405		*len = 0;
406		UNLOCK_VOL(vol);
407		return EISDIR;
408
409	}
410
411	DPRINTF(0, ("dosfs_write called %" B_PRIuSIZE " bytes at %" B_PRIdOFF
412		" from buffer at %p (vnode id %" B_PRIdINO ")\n", *len, pos, buf,
413		node->vnid));
414
415	if ((cookie->mode & O_RWMASK) == O_RDONLY) {
416		dprintf("dosfs_write: called on file opened as read-only\n");
417		*len = 0;
418		result = EPERM;
419		goto bi;
420	}
421
422	if (pos < 0) pos = 0;
423
424	if (cookie->mode & O_APPEND) {
425		pos = node->st_size;
426	}
427
428	if (pos >= MAX_FILE_SIZE) {
429		dprintf("dosfs_write: write position exceeds fat limits\n");
430		*len = 0;
431		result = E2BIG;
432		goto bi;
433	}
434
435	if (pos + *len >= MAX_FILE_SIZE) {
436		*len = (size_t)(MAX_FILE_SIZE - pos);
437	}
438
439	// extend file size if needed
440	if (pos + *len > node->st_size) {
441		uint32 clusters = (pos + *len + vol->bytes_per_sector*vol->sectors_per_cluster - 1) / vol->bytes_per_sector / vol->sectors_per_cluster;
442		if (node->st_size <= (clusters - 1) * vol->sectors_per_cluster * vol->bytes_per_sector) {
443			if ((result = set_fat_chain_length(vol, node, clusters, false))
444					!= B_OK) {
445				goto bi;
446			}
447			node->iteration++;
448		}
449		node->st_size = pos + *len;
450		/* needs to be written to disk asap so that later vnid calculations
451		 * by get_next_dirent are correct
452		 */
453		write_vnode_entry(vol, node);
454
455		DPRINTF(0, ("setting file size to %" B_PRIdOFF " (%" B_PRIu32
456			" clusters)\n", node->st_size, clusters));
457		node->dirty = true;
458		file_cache_set_size(node->cache, node->st_size);
459		file_map_set_size(node->file_map, node->st_size);
460	}
461
462	result = file_cache_write(node->cache, cookie, pos, buf, len);
463
464bi:
465	if (result != B_OK) {
466		DPRINTF(0, ("dosfs_write (%s)\n", strerror(result)));
467	} else {
468		DPRINTF(0, ("dosfs_write: wrote %" B_PRIuSIZE " bytes\n", *len));
469	}
470	UNLOCK_VOL(vol);
471
472	return result;
473}
474
475
476status_t
477dosfs_close(fs_volume *_vol, fs_vnode *_node, void *_cookie)
478{
479	nspace	*vol = (nspace *)_vol->private_volume;
480	vnode	*node = (vnode *)_node->private_node;
481
482	LOCK_VOL(vol);
483
484	DPRINTF(0, ("dosfs_close (vnode id %" B_PRIdINO ")\n", node->vnid));
485
486	if ((vol->fs_flags & FS_FLAGS_OP_SYNC) && node->dirty) {
487		_dosfs_sync(vol);
488		node->dirty = false;
489	}
490
491	UNLOCK_VOL(vol);
492
493	return 0;
494}
495
496
497status_t
498dosfs_free_cookie(fs_volume *_vol, fs_vnode *_node, void *_cookie)
499{
500	nspace *vol = _vol->private_volume;
501	vnode *node = _node->private_node;
502	filecookie *cookie = _cookie;
503	LOCK_VOL(vol);
504
505	DPRINTF(0, ("dosfs_free_cookie (vnode id %" B_PRIdINO ")\n", node->vnid));
506
507	free(cookie);
508
509	UNLOCK_VOL(vol);
510
511	return 0;
512}
513
514
515status_t
516dosfs_create(fs_volume *_vol, fs_vnode *_dir, const char *name, int omode,
517	int perms, void **_cookie, ino_t *vnid)
518{
519	nspace *vol = (nspace *)_vol->private_volume;
520	vnode *dir = (vnode *)_dir->private_node, *file;
521	filecookie *cookie;
522	status_t result = EINVAL;
523	bool dups_exist;
524
525	LOCK_VOL(vol);
526
527	ASSERT(name != NULL);
528	if (name == NULL) {
529		dprintf("dosfs_create called with null name\n");
530		UNLOCK_VOL(vol);
531		return EINVAL;
532	}
533
534	DPRINTF(0, ("dosfs_create called: %" B_PRIdINO "/%s perms=%o omode=%o\n",
535		dir->vnid, name, perms, omode));
536
537	if (vol->flags & B_FS_IS_READONLY) {
538		dprintf("dosfs_create called on read-only volume\n");
539		UNLOCK_VOL(vol);
540		return EROFS;
541	}
542
543	// TODO : is it needed ? vfs job ?
544	/*if (is_vnode_removed(vol->id, dir->vnid) > 0) {
545		dprintf("dosfs_create() called in removed directory. disallowed.\n");
546		UNLOCK_VOL(vol);
547		return EPERM;
548	}*/
549
550	if ((omode & O_RWMASK) == O_RDONLY) {
551		dprintf("invalid permissions used in creating file\n");
552		UNLOCK_VOL(vol);
553		return EPERM;
554	}
555
556	// create file cookie; do it here to make cleaning up easier
557	if ((cookie = calloc(sizeof(filecookie), 1)) == NULL) {
558		result = ENOMEM;
559		goto bi;
560	}
561
562	result = findfile_case_duplicates(vol, dir, name, vnid, &file, &dups_exist);
563	if (result == B_OK) {
564		if (omode & O_EXCL) {
565			dprintf("exclusive dosfs_create called on existing file %s\n", name);
566			put_vnode(_vol, file->vnid);
567			result = EEXIST;
568			goto bi;
569		}
570
571		if (file->mode & FAT_SUBDIR) {
572			dprintf("can't dosfs_create over an existing subdirectory\n");
573			put_vnode(_vol, file->vnid);
574			result = EPERM;
575			goto bi;
576		}
577
578		if (file->disk_image) {
579			dprintf("can't dosfs_create over a disk image\n");
580			put_vnode(_vol, file->vnid);
581			result = EPERM;
582			goto bi;
583		}
584
585		if (omode & O_TRUNC) {
586			set_fat_chain_length(vol, file, 0, false);
587			file->st_size = 0;
588			file->iteration++;
589		}
590	} else if (result == ENOENT && dups_exist) {
591		// the file doesn't exist in the exact case, but another does in the
592		// non-exact case. We wont create the new file.
593		result = EEXIST;
594		goto bi;
595	} else if (result == ENOENT && !dups_exist) {
596		// the file doesn't already exist in any case
597		vnode dummy; /* used only to create directory entry */
598
599		dummy.dir_vnid = dir->vnid;
600		dummy.cluster = 0;
601		dummy.end_cluster = 0;
602		dummy.mode = 0;
603		dummy.st_size = 0;
604		time(&(dummy.st_time));
605		dummy.st_crtim = dummy.st_time;
606
607		if ((result = create_dir_entry(vol, dir, &dummy, name, &(dummy.sindex), &(dummy.eindex))) != B_OK) {
608			dprintf("dosfs_create: error creating directory entry for %s (%s)\n", name, strerror(result));
609			goto bi;
610		}
611		dummy.vnid = GENERATE_DIR_INDEX_VNID(dummy.dir_vnid, dummy.sindex);
612		// XXX: dangerous construct
613		if (find_vnid_in_vcache(vol, dummy.vnid) == B_OK) {
614			dummy.vnid = generate_unique_vnid(vol);
615			if ((result = add_to_vcache(vol, dummy.vnid, GENERATE_DIR_INDEX_VNID(dummy.dir_vnid, dummy.sindex))) < 0) {
616				// XXX: should remove entry on failure
617				if (vol->fs_flags & FS_FLAGS_OP_SYNC)
618					_dosfs_sync(vol);
619				goto bi;
620			}
621		}
622		*vnid = dummy.vnid;
623
624		result = get_vnode(_vol, *vnid, (void **)&file);
625		if (result < B_OK) {
626			if (vol->fs_flags & FS_FLAGS_OP_SYNC)
627				_dosfs_sync(vol);
628			goto bi;
629		}
630	} else {
631		goto bi;
632	}
633
634	cookie->mode = omode;
635	*_cookie = cookie;
636
637	notify_entry_created(vol->id, dir->vnid, name, *vnid);
638
639	result = 0;
640
641	if (vol->fs_flags & FS_FLAGS_OP_SYNC)
642		_dosfs_sync(vol);
643
644bi:	if (result != B_OK) free(cookie);
645
646	UNLOCK_VOL(vol);
647
648	if (result != B_OK) DPRINTF(0, ("dosfs_create (%s)\n", strerror(result)));
649
650	return result;
651}
652
653
654status_t
655dosfs_mkdir(fs_volume *_vol, fs_vnode *_dir, const char *name, int perms)
656{
657	nspace *vol = (nspace *)_vol->private_volume;
658	vnode *dir = (vnode *)_dir->private_node, dummy;
659	status_t result = EINVAL;
660	struct csi csi;
661	uchar *buffer;
662	uint32 i;
663
664	LOCK_VOL(vol);
665
666	// TODO : is it needed ? vfs job ?
667	/*if (is_vnode_removed(vol->id, dir->vnid) > 0) {
668		dprintf("dosfs_mkdir() called in removed directory. disallowed.\n");
669		UNLOCK_VOL(vol);
670		return EPERM;
671	}*/
672
673	DPRINTF(0, ("dosfs_mkdir called: %" B_PRIdINO "/%s (perm %o)\n", dir->vnid,
674		name, perms));
675
676	if ((dir->mode & FAT_SUBDIR) == 0) {
677		dprintf("dosfs_mkdir: vnode id %" B_PRIdINO " is not a directory\n",
678			dir->vnid);
679		UNLOCK_VOL(vol);
680		return EINVAL;
681	}
682
683	// S_IFDIR is never set in perms, so we patch it
684	perms &= ~S_IFMT; perms |= S_IFDIR;
685
686	if (vol->flags & B_FS_IS_READONLY) {
687		dprintf("mkdir called on read-only volume\n");
688		UNLOCK_VOL(vol);
689		return EROFS;
690	}
691
692	/* only used to create directory entry */
693	dummy.dir_vnid = dir->vnid;
694	if ((result = allocate_n_fat_entries(vol, 1, (int32 *)&(dummy.cluster))) < 0) {
695		dprintf("dosfs_mkdir: error allocating space for %s (%s))\n", name, strerror(result));
696		goto bi;
697	}
698	dummy.end_cluster = dummy.cluster;
699	dummy.mode = FAT_SUBDIR;
700	if (!(perms & (S_IWUSR | S_IWGRP | S_IWGRP))) {
701		dummy.mode |= FAT_READ_ONLY;
702	}
703	dummy.st_size = vol->bytes_per_sector*vol->sectors_per_cluster;
704	time(&(dummy.st_time));
705	dummy.st_crtim = dummy.st_time;
706
707	dummy.vnid = GENERATE_DIR_CLUSTER_VNID(dummy.dir_vnid, dummy.cluster);
708	// XXX: dangerous construct
709	if (find_vnid_in_vcache(vol, dummy.vnid) == B_OK) {
710		dummy.vnid = generate_unique_vnid(vol);
711		if ((result = add_to_vcache(vol, dummy.vnid, GENERATE_DIR_CLUSTER_VNID(dummy.dir_vnid, dummy.cluster))) < 0)
712			goto bi2;
713	}
714
715	if ((result = dlist_add(vol, dummy.vnid)) < 0) {
716		dprintf("dosfs_mkdir: error adding directory %s to dlist (%s)\n", name, strerror(result));
717		goto bi3;
718	}
719
720	buffer = malloc(vol->bytes_per_sector);
721	if (!buffer) {
722		result = ENOMEM;
723		goto bi4;
724	}
725
726	if ((result = create_dir_entry(vol, dir, &dummy, name, &(dummy.sindex), &(dummy.eindex))) != B_OK) {
727		dprintf("dosfs_mkdir: error creating directory entry for %s (%s))\n", name, strerror(result));
728		goto bi5;
729	}
730
731	// create '.' and '..' entries and then end of directories
732	memset(buffer, 0, vol->bytes_per_sector);
733	memset(buffer, ' ', 11);
734	memset(buffer+0x20, ' ', 11);
735	buffer[0] = buffer[0x20] = buffer[0x21] = '.';
736	buffer[0x0b] = buffer[0x2b] = FAT_SUBDIR;
737	i = time_t2dos(dummy.st_time);
738	buffer[0x0e] = i & 0xff;
739	buffer[0x0f] = (i >> 8) & 0xff;
740	buffer[0x10] = (i >> 16) & 0xff;
741	buffer[0x11] = (i >> 24) & 0xff;
742	buffer[0x16] = i & 0xff;
743	buffer[0x17] = (i >> 8) & 0xff;
744	buffer[0x18] = (i >> 16) & 0xff;
745	buffer[0x19] = (i >> 24) & 0xff;
746	i = time_t2dos(dir->st_crtim);
747	buffer[0x2e] = i & 0xff;
748	buffer[0x2f] = (i >> 8) & 0xff;
749	buffer[0x30] = (i >> 16) & 0xff;
750	buffer[0x31] = (i >> 24) & 0xff;
751	i = time_t2dos(dir->st_time);
752	buffer[0x36] = i & 0xff;
753	buffer[0x37] = (i >> 8) & 0xff;
754	buffer[0x38] = (i >> 16) & 0xff;
755	buffer[0x39] = (i >> 24) & 0xff;
756	buffer[0x1a] = dummy.cluster & 0xff;
757	buffer[0x1b] = (dummy.cluster >> 8) & 0xff;
758	if (vol->fat_bits == 32) {
759		buffer[0x14] = (dummy.cluster >> 16) & 0xff;
760		buffer[0x15] = (dummy.cluster >> 24) & 0xff;
761	}
762	// root directory is always denoted by cluster 0, even for fat32 (!)
763	if (dir->vnid != vol->root_vnode.vnid) {
764		buffer[0x3a] = dir->cluster & 0xff;
765		buffer[0x3b] = (dir->cluster >> 8) & 0xff;
766		if (vol->fat_bits == 32) {
767			buffer[0x34] = (dir->cluster >> 16) & 0xff;
768			buffer[0x35] = (dir->cluster >> 24) & 0xff;
769		}
770	}
771
772	init_csi(vol, dummy.cluster, 0, &csi);
773	csi_write_block(&csi, buffer);
774
775	// clear out rest of cluster to keep scandisk happy
776	memset(buffer, 0, vol->bytes_per_sector);
777	for (i=1;i<vol->sectors_per_cluster;i++) {
778		if (iter_csi(&csi, 1) != B_OK) {
779			dprintf("dosfs_mkdir: error writing directory cluster\n");
780			break;
781		}
782		csi_write_block(&csi, buffer);
783	}
784
785	free(buffer);
786
787	notify_entry_created(vol->id, dir->vnid, name, dummy.vnid);
788
789	result = B_OK;
790
791	if (vol->fs_flags & FS_FLAGS_OP_SYNC)
792		_dosfs_sync(vol);
793
794	UNLOCK_VOL(vol);
795	return result;
796
797bi5:
798	free(buffer);
799bi4:
800	dlist_remove(vol, dummy.vnid);
801bi3:
802	if (IS_ARTIFICIAL_VNID(dummy.vnid))
803		remove_from_vcache(vol, dummy.vnid);
804bi2:
805	clear_fat_chain(vol, dummy.cluster, false);
806	if (vol->fs_flags & FS_FLAGS_OP_SYNC)
807		_dosfs_sync(vol);
808bi:
809	UNLOCK_VOL(vol);
810	if (result != B_OK) DPRINTF(0, ("dosfs_mkdir (%s)\n", strerror(result)));
811	return result;
812}
813
814
815status_t
816dosfs_rename(fs_volume *_vol, fs_vnode *_odir, const char *oldname,
817	fs_vnode *_ndir, const char *newname)
818{
819	status_t result = EINVAL;
820	nspace *vol = (nspace *)_vol->private_volume;
821	vnode *odir = (vnode *)_odir->private_node;
822	vnode *ndir = (vnode *)_ndir->private_node;
823	vnode *file, *file2;
824	uint32 ns, ne;
825	bool dups_exist;
826	bool dirty = false;
827
828	LOCK_VOL(vol);
829
830	DPRINTF(0, ("dosfs_rename called: %" B_PRIdINO "/%s->%" B_PRIdINO "/%s\n",
831		odir->vnid, oldname, ndir->vnid, newname));
832
833	if (!oldname || !(*oldname) || !newname || !(*newname)) {
834		result = EINVAL;
835		goto bi;
836	}
837
838	if(!is_filename_legal(newname)) {
839		dprintf("dosfs_rename called with invalid name '%s'\n", newname);
840		result = EINVAL;
841		goto bi;
842	}
843
844	if (vol->flags & B_FS_IS_READONLY) {
845		dprintf("rename called on read-only volume\n");
846		result = EROFS;
847		goto bi;
848	}
849
850	if ((odir->vnid == ndir->vnid) && !strcmp(oldname, newname)) {
851		result = EPERM;
852		goto bi;
853	}
854
855	// locate the file
856	if ((result = findfile_case(vol,odir,oldname,NULL,&file)) != B_OK) {
857		DPRINTF(0, ("dosfs_rename: can't find file %s in directory %" B_PRIdINO
858			"\n", oldname, odir->vnid));
859		goto bi;
860	}
861
862	if (file->disk_image) {
863		dprintf("rename called on disk image or disk image directory\n");
864		result = EPERM;
865		goto bi1;
866	}
867
868	// don't move a directory into one of its children
869	if (file->mode & FAT_SUBDIR) {
870		ino_t vnid = ndir->vnid;
871		while (1) {
872			vnode *dir;
873			ino_t parent;
874
875			if (vnid == file->vnid) {
876				result = EINVAL;
877				goto bi1;
878			}
879
880			if (vnid == vol->root_vnode.vnid)
881				break;
882
883			result = get_vnode(_vol, vnid, (void **)&dir);
884			if (result < B_OK)
885				goto bi1;
886			parent = dir->dir_vnid;
887			put_vnode(_vol, vnid);
888			vnid = parent;
889		}
890	}
891
892	// see if file already exists and erase it if it does
893	result = findfile_case_duplicates(vol, ndir, newname, NULL, &file2, &dups_exist);
894	if (result == B_OK) {
895		if (file2->mode & FAT_SUBDIR) {
896			dprintf("destination already occupied by a directory\n");
897			result = EPERM;
898			goto bi2;
899		}
900
901		if (file2->disk_image) {
902			DPRINTF(0, ("dosfs_rename: can't replace disk image or disk image directory\n"));
903			result = EPERM;
904			goto bi2;
905		}
906		ns = file2->sindex; ne = file2->eindex;
907
908		// let others know the old file is gone
909		notify_entry_removed(vol->id, ndir->vnid, oldname, file2->vnid);
910
911		// Make sure this vnode 1) is in the vcache and 2) no longer has a
912		// location associated with it. See discussion in dosfs_unlink()
913		vcache_set_entry(vol, file2->vnid, generate_unique_vnid(vol));
914
915		// mark vnode for removal (dosfs_remove_vnode will clear the fat chain)
916		// note we don't have to lock the file because the fat chain doesn't
917		// get wiped from the disk until dosfs_remove_vnode() is called; we'll
918		// have a phantom chain in effect until the last file is closed.
919		remove_vnode(_vol, file2->vnid); // must be done in this order
920		put_vnode(_vol, file2->vnid);
921
922		dirty = true;
923
924		// erase old directory entry
925		if ((result = erase_dir_entry(vol, file)) != B_OK) {
926			dprintf("dosfs_rename: error erasing old directory entry for %s (%s)\n", newname, strerror(result));
927			goto bi1;
928		}
929	} else if (result == ENOENT && (!dups_exist || (odir->vnid == ndir->vnid && !strcasecmp(oldname, newname)))) {
930		// there isn't an entry and there are no duplicates in the target dir or
931		// there isn't an entry and the target dir is the same as the source dir and
932		//   the source and target name are the same, case-insensitively
933
934		// erase old directory entry
935		if ((result = erase_dir_entry(vol, file)) != B_OK) {
936			dprintf("dosfs_rename: error erasing old directory entry for %s (%s)\n", newname, strerror(result));
937			goto bi1;
938		}
939
940		dirty = true;
941
942		// create the new directory entry
943		if ((result = create_dir_entry(vol, ndir, file, newname, &ns, &ne)) != B_OK) {
944			dprintf("dosfs_rename: error creating directory entry for %s\n", newname);
945			goto bi1;
946		}
947	} else if (result == ENOENT && dups_exist) {
948		// the entry doesn't exist but a non-case entry does, so we can't do it
949		result = EEXIST;
950		goto bi1;
951	} else {
952		goto bi1;
953	}
954
955	// shrink the directory (an error here is not disastrous)
956	compact_directory(vol, odir);
957
958	dirty = true;
959
960	// update vnode information
961	file->dir_vnid = ndir->vnid;
962	file->sindex = ns;
963	file->eindex = ne;
964
965	// update vcache
966	vcache_set_entry(vol, file->vnid,
967			(file->st_size) ?
968			GENERATE_DIR_CLUSTER_VNID(file->dir_vnid, file->cluster) :
969			GENERATE_DIR_INDEX_VNID(file->dir_vnid, file->sindex));
970
971	// XXX: only write changes in the directory entry if needed
972	//      (i.e. old entry, not new)
973	write_vnode_entry(vol, file);
974
975	if (file->mode & FAT_SUBDIR) {
976		// update '..' directory entry if needed
977		// this should most properly be in write_vnode, but it is safe
978		// to keep it here since this is the only way the cluster of
979		// the parent can change.
980		struct diri diri;
981		uint8 *buffer;
982		if ((buffer = diri_init(vol, file->cluster, 1, &diri)) == NULL) {
983			dprintf("error opening directory :(\n");
984			result = EIO;
985			goto bi2;
986		}
987
988		diri_make_writable(&diri);
989
990		if (memcmp(buffer, "..         ", 11)) {
991			dprintf("invalid directory :(\n");
992			result = EIO;
993			goto bi2;
994		}
995		if (ndir->vnid == vol->root_vnode.vnid) {
996			// root directory always has cluster = 0
997			buffer[0x1a] = buffer[0x1b] = 0;
998		} else {
999			buffer[0x1a] = ndir->cluster & 0xff;
1000			buffer[0x1b] = (ndir->cluster >> 8) & 0xff;
1001			if (vol->fat_bits == 32) {
1002				buffer[0x14] = (ndir->cluster >> 16) & 0xff;
1003				buffer[0x15] = (ndir->cluster >> 24) & 0xff;
1004			}
1005		}
1006		diri_free(&diri);
1007	}
1008
1009#if TRACK_FILENAME
1010	if (file->filename) free(file->filename);
1011	file->filename = malloc(strlen(newname) + 1);
1012	if (file->filename) strcpy(file->filename, newname);
1013#endif
1014
1015	notify_entry_moved(vol->id, odir->vnid, oldname, ndir->vnid, newname,
1016		file->vnid);
1017
1018	// update MIME information
1019	if(!(file->mode & FAT_SUBDIR)) {
1020		set_mime_type(file, newname);
1021		notify_attribute_changed(vol->id, -1, file->vnid, "BEOS:TYPE",
1022			B_ATTR_CHANGED);
1023	}
1024
1025	result = 0;
1026
1027bi2:
1028	if (result != B_OK)
1029		put_vnode(_vol, file2->vnid);
1030bi1:
1031	put_vnode(_vol, file->vnid);
1032bi:
1033	if ((vol->fs_flags & FS_FLAGS_OP_SYNC) && dirty)
1034		_dosfs_sync(vol);
1035	UNLOCK_VOL(vol);
1036	if (result != B_OK) DPRINTF(0, ("dosfs_rename (%s)\n", strerror(result)));
1037	return result;
1038}
1039
1040
1041status_t
1042dosfs_remove_vnode(fs_volume *_vol, fs_vnode *_node, bool reenter)
1043{
1044	nspace *vol = (nspace *)_vol->private_volume;
1045	vnode *node = (vnode *)_node->private_node;
1046
1047	LOCK_VOL(vol);
1048
1049	DPRINTF(0, ("dosfs_remove_vnode (%" B_PRIdINO ")\n", node->vnid));
1050
1051	if (vol->flags & B_FS_IS_READONLY) {
1052		dprintf("dosfs_remove_vnode: read-only volume\n");
1053		UNLOCK_VOL(vol);
1054		return EROFS;
1055	}
1056
1057	// clear the fat chain
1058	ASSERT((node->cluster == 0) || IS_DATA_CLUSTER(node->cluster));
1059	/* XXX: the following assertion was tripped */
1060	ASSERT((node->cluster != 0) || (node->st_size == 0));
1061	if (node->cluster != 0)
1062		clear_fat_chain(vol, node->cluster, (node->mode & FAT_SUBDIR) != 0);
1063
1064	/* remove vnode id from the cache */
1065	if (find_vnid_in_vcache(vol, node->vnid) == B_OK)
1066		remove_from_vcache(vol, node->vnid);
1067
1068	/* at this point, the node shouldn't be in the dlist anymore */
1069	if ((node->mode & FAT_SUBDIR) != 0) {
1070		ASSERT(dlist_find(vol, CLUSTER_OF_DIR_CLUSTER_VNID(node->vnid)) == -1);
1071	}
1072
1073	free(node);
1074
1075	if (!reenter && vol->fs_flags & FS_FLAGS_OP_SYNC) {
1076		// sync the entire filesystem,
1077		// but only if we're not reentrant. Presumably the
1078		// function that called this will sync.
1079		_dosfs_sync(vol);
1080	}
1081
1082	UNLOCK_VOL(vol);
1083
1084	return B_OK;
1085}
1086
1087
1088// get rid of node or directory
1089static status_t
1090do_unlink(fs_volume *_vol, fs_vnode *_dir, const char *name, bool is_file)
1091{
1092	status_t result = EINVAL;
1093	nspace *vol = (nspace *)_vol->private_volume;
1094	vnode *dir = (vnode *)_dir->private_node, *file;
1095	ino_t vnid;
1096
1097	if (!strcmp(name, "."))
1098		return EPERM;
1099
1100	if (!strcmp(name, ".."))
1101		return EPERM;
1102
1103	LOCK_VOL(vol);
1104
1105	DPRINTF(0, ("do_unlink %" B_PRIdINO "/%s\n", dir->vnid, name));
1106
1107	if (vol->flags & B_FS_IS_READONLY) {
1108		dprintf("do_unlink: read-only volume\n");
1109		result = EROFS;
1110		goto bi;
1111	}
1112
1113	// locate the file
1114	if ((result = findfile_case(vol,dir,name,&vnid,&file)) != B_OK) {
1115		DPRINTF(0, ("do_unlink: can't find file %s in directory %" B_PRIdINO
1116			"\n", name, dir->vnid));
1117		result = ENOENT;
1118		goto bi;
1119	}
1120
1121	if (file->disk_image) {
1122		DPRINTF(0, ("do_unlink: can't unlink disk image or disk image directory\n"));
1123		result = EPERM;
1124		goto bi1;
1125	}
1126
1127	// don't need to check file permissions because it will be done for us
1128	// also don't need to lock the file (see dosfs_rename for reasons why)
1129	if (is_file) {
1130		if (file->mode & FAT_SUBDIR) {
1131			result = EISDIR;
1132			goto bi1;
1133		}
1134	} else {
1135		if ((file->mode & FAT_SUBDIR) == 0) {
1136			result = ENOTDIR;
1137			goto bi1;
1138		}
1139
1140		if (file->vnid == vol->root_vnode.vnid) {
1141			// this actually isn't a problem since the root vnode
1142			// will always be busy while the volume mounted
1143			dprintf("dosfs_rmdir: don't call this on the root directory\n");
1144			result = EPERM;
1145			goto bi1;
1146		}
1147
1148		if ((result = check_dir_empty(vol, file)) < 0) {
1149			if (result == ENOTEMPTY) DPRINTF(0, ("dosfs_rmdir called on non-empty directory\n"));
1150			goto bi1;
1151		}
1152	}
1153
1154	// erase the entry in the parent directory
1155	if ((result = erase_dir_entry(vol, file)) != B_OK)
1156		goto bi1;
1157
1158	// shrink the parent directory (errors here are not disastrous)
1159	compact_directory(vol, dir);
1160
1161	notify_entry_removed(vol->id, dir->vnid, name, file->vnid);
1162
1163	/* Set the loc to a unique value. This effectively removes it from the
1164	 * vcache without releasing its vnid for reuse. It also nicely reserves
1165	 * the vnid from use by other nodes. This is okay because the vnode is
1166	 * locked in memory after this point and loc will not be referenced from
1167	 * here on.
1168	 */
1169	vcache_set_entry(vol, file->vnid, generate_unique_vnid(vol));
1170
1171	if (!is_file)
1172		dlist_remove(vol, file->vnid);
1173
1174	// fsil doesn't call dosfs_write_vnode for us, so we have to free the
1175	// vnode manually here.
1176	remove_vnode(_vol, file->vnid);
1177
1178	result = 0;
1179
1180	if (vol->fs_flags & FS_FLAGS_OP_SYNC)
1181		_dosfs_sync(vol);
1182
1183bi1:
1184	put_vnode(_vol, vnid);		// get 1 free
1185bi:
1186	UNLOCK_VOL(vol);
1187
1188	if (result != B_OK) DPRINTF(0, ("do_unlink (%s)\n", strerror(result)));
1189
1190	return result;
1191}
1192
1193
1194status_t
1195dosfs_unlink(fs_volume *vol, fs_vnode *dir, const char *name)
1196{
1197	DPRINTF(1, ("dosfs_unlink called\n"));
1198
1199	return do_unlink(vol, dir, name, true);
1200}
1201
1202
1203status_t
1204dosfs_rmdir(fs_volume *vol, fs_vnode *dir, const char *name)
1205{
1206	DPRINTF(1, ("dosfs_rmdir called\n"));
1207
1208	return do_unlink(vol, dir, name, false);
1209}
1210
1211
1212bool
1213dosfs_can_page(fs_volume *_vol, fs_vnode *_node, void *_cookie)
1214{
1215	// ToDo: we're obviously not even asked...
1216	return false;
1217}
1218
1219
1220status_t
1221dosfs_read_pages(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t pos,
1222	const iovec *vecs, size_t count, size_t *_numBytes)
1223{
1224	nspace *vol = (nspace *)_vol->private_volume;
1225	vnode *node = (vnode *)_node->private_node;
1226	uint32 vecIndex = 0;
1227	size_t vecOffset = 0;
1228	size_t bytesLeft = *_numBytes;
1229	status_t status;
1230
1231	if (node->cache == NULL)
1232		return(B_BAD_VALUE);
1233
1234	LOCK_VOL(vol);
1235
1236	while (true) {
1237		struct file_io_vec fileVecs[8];
1238		size_t fileVecCount = 8;
1239		bool bufferOverflow;
1240		size_t bytes = bytesLeft;
1241
1242		status = file_map_translate(node->file_map, pos, bytesLeft, fileVecs,
1243			&fileVecCount, 0);
1244		if (status != B_OK && status != B_BUFFER_OVERFLOW)
1245			break;
1246
1247		bufferOverflow = status == B_BUFFER_OVERFLOW;
1248
1249		status = read_file_io_vec_pages(vol->fd, fileVecs,
1250			fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
1251		if (status != B_OK || !bufferOverflow)
1252			break;
1253
1254		pos += bytes;
1255		bytesLeft -= bytes;
1256	}
1257
1258	UNLOCK_VOL(vol);
1259
1260	return status;
1261}
1262
1263
1264status_t
1265dosfs_write_pages(fs_volume *_vol, fs_vnode *_node, void *_cookie, off_t pos,
1266	const iovec *vecs, size_t count, size_t *_numBytes)
1267{
1268	nspace *vol = (nspace *)_vol->private_volume;
1269	vnode *node = (vnode *)_node->private_node;
1270	uint32 vecIndex = 0;
1271	size_t vecOffset = 0;
1272	size_t bytesLeft = *_numBytes;
1273	status_t status;
1274
1275	if (node->cache == NULL)
1276		return B_BAD_VALUE;
1277
1278	LOCK_VOL(vol);
1279
1280	if ((vol->flags & B_FS_IS_READONLY) != 0) {
1281		UNLOCK_VOL(vol);
1282		return EROFS;
1283	}
1284
1285	while (true) {
1286		struct file_io_vec fileVecs[8];
1287		size_t fileVecCount = 8;
1288		bool bufferOverflow;
1289		size_t bytes = bytesLeft;
1290
1291		status = file_map_translate(node->file_map, pos, bytesLeft, fileVecs,
1292			&fileVecCount, 0);
1293		if (status != B_OK && status != B_BUFFER_OVERFLOW)
1294			break;
1295
1296		bufferOverflow = status == B_BUFFER_OVERFLOW;
1297
1298		status = write_file_io_vec_pages(vol->fd, fileVecs,
1299			fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
1300		if (status != B_OK || !bufferOverflow)
1301			break;
1302
1303		pos += bytes;
1304		bytesLeft -= bytes;
1305	}
1306
1307	UNLOCK_VOL(vol);
1308
1309	return status;
1310}
1311
1312
1313status_t
1314dosfs_get_file_map(fs_volume *_vol, fs_vnode *_node, off_t position,
1315	size_t length, struct file_io_vec *vecs, size_t *_count)
1316{
1317	nspace *vol = (nspace *)_vol->private_volume;
1318	vnode *node = (vnode *)_node->private_node;
1319	struct csi iter;
1320	status_t result;
1321	uint32 skipSectors;
1322	off_t offset;
1323	size_t index = 0;
1324	size_t max = *_count;
1325
1326	LOCK_VOL(vol);
1327	*_count = 0;
1328
1329	if ((node->mode & FAT_SUBDIR) != 0) {
1330		DPRINTF(0, ("dosfs_get_file_map called on subdirectory %" B_PRIdINO
1331			"\n", node->vnid));
1332		UNLOCK_VOL(vol);
1333		return EISDIR;
1334	}
1335
1336	DPRINTF(0, ("dosfs_get_file_map called %" B_PRIuSIZE " bytes at %" B_PRIdOFF
1337		" (vnode id %" B_PRIdINO ")\n", length, position, node->vnid));
1338
1339	if (position < 0)
1340		position = 0;
1341
1342	if (node->st_size == 0 || length == 0 || position >= node->st_size) {
1343		result = B_OK;
1344		goto bi;
1345	}
1346
1347	// Truncate to file size, taking overflow into account.
1348	if (position + length >= node->st_size || position + length < position)
1349		length = node->st_size - position;
1350
1351	result = init_csi(vol, node->cluster, 0, &iter);
1352	if (result != B_OK) {
1353		dprintf("dosfs_get_file_map: invalid starting cluster (%" B_PRIu32
1354			")\n", node->cluster);
1355		result = EIO;
1356		goto bi;
1357	}
1358
1359	skipSectors = position / vol->bytes_per_sector;
1360	if (skipSectors > 0) {
1361		result = iter_csi(&iter, skipSectors);
1362		if (result != B_OK) {
1363			dprintf("dosfs_get_file_map: end of file reached (init)\n");
1364			result = EIO;
1365			goto bi;
1366		}
1367	}
1368
1369	ASSERT(iter.cluster == get_nth_fat_entry(vol, node->cluster,
1370			position / vol->bytes_per_sector / vol->sectors_per_cluster));
1371
1372	offset = position % vol->bytes_per_sector;
1373	while (length > 0) {
1374		off_t block = csi_to_block(&iter);
1375		uint32 sectors = 1;
1376
1377		length -= min(length, vol->bytes_per_sector - offset);
1378
1379		while (length > 0) {
1380			result = iter_csi(&iter, 1);
1381			if (result != B_OK) {
1382				dprintf("dosfs_get_file_map: end of file reached\n");
1383				result = EIO;
1384				goto bi;
1385			}
1386
1387			if (block + sectors != csi_to_block(&iter)) {
1388				// Disjoint sectors, need to flush and begin a new vector.
1389				break;
1390			}
1391
1392			length -= min(length, vol->bytes_per_sector);
1393			sectors++;
1394		}
1395
1396		vecs[index].offset = block * vol->bytes_per_sector + offset;
1397		vecs[index].length = sectors * vol->bytes_per_sector - offset;
1398		index++;
1399
1400		if (length == 0)
1401			break;
1402
1403		if (index >= max) {
1404			// we're out of file_io_vecs; let's bail out
1405			result = B_BUFFER_OVERFLOW;
1406			goto bi;
1407		}
1408
1409		offset = 0;
1410	}
1411
1412	result = B_OK;
1413bi:
1414	*_count = index;
1415
1416	if (result != B_OK) {
1417		DPRINTF(0, ("dosfs_get_file_map (%s)\n", strerror(result)));
1418	}
1419	UNLOCK_VOL(vol);
1420
1421	return result;
1422}
1423