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