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