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