1/*
2 * Copyright 2002-2008, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6//! Operations on file descriptors
7
8#include "fd.h"
9
10#include <stdlib.h>
11
12#include "fssh_atomic.h"
13#include "fssh_fcntl.h"
14#include "fssh_kernel_export.h"
15#include "fssh_kernel_priv.h"
16#include "fssh_string.h"
17#include "fssh_uio.h"
18#include "syscalls.h"
19
20
21//#define TRACE_FD
22#ifdef TRACE_FD
23#	define TRACE(x) dprintf x
24#else
25#	define TRACE(x)
26#endif
27
28
29namespace FSShell {
30
31
32io_context* gKernelIOContext;
33
34
35/*** General fd routines ***/
36
37
38#ifdef DEBUG
39void dump_fd(int fd, struct file_descriptor *descriptor);
40
41void
42dump_fd(int fd,struct file_descriptor *descriptor)
43{
44	fssh_dprintf("fd[%d] = %p: type = %d, ref_count = %d, ops = %p, u.vnode = %p, u.mount = %p, cookie = %p, open_mode = %x, pos = %lld\n",
45		fd, descriptor, (int)descriptor->type, (int)descriptor->ref_count, descriptor->ops,
46		descriptor->u.vnode, descriptor->u.mount, descriptor->cookie, (int)descriptor->open_mode, descriptor->pos);
47}
48#endif
49
50
51/** Allocates and initializes a new file_descriptor */
52
53struct file_descriptor *
54alloc_fd(void)
55{
56	struct file_descriptor *descriptor;
57
58	descriptor = (file_descriptor*)malloc(sizeof(struct file_descriptor));
59	if (descriptor == NULL)
60		return NULL;
61
62	descriptor->u.vnode = NULL;
63	descriptor->cookie = NULL;
64	descriptor->ref_count = 1;
65	descriptor->open_count = 0;
66	descriptor->open_mode = 0;
67	descriptor->pos = 0;
68
69	return descriptor;
70}
71
72
73bool
74fd_close_on_exec(struct io_context *context, int fd)
75{
76	return CHECK_BIT(context->fds_close_on_exec[fd / 8], fd & 7) ? true : false;
77}
78
79
80void
81fd_set_close_on_exec(struct io_context *context, int fd, bool closeFD)
82{
83	if (closeFD)
84		context->fds_close_on_exec[fd / 8] |= (1 << (fd & 7));
85	else
86		context->fds_close_on_exec[fd / 8] &= ~(1 << (fd & 7));
87}
88
89
90/** Searches a free slot in the FD table of the provided I/O context, and inserts
91 *	the specified descriptor into it.
92 */
93
94int
95new_fd_etc(struct io_context *context, struct file_descriptor *descriptor,
96	int firstIndex)
97{
98	int fd = -1;
99	uint32_t i;
100
101	fssh_mutex_lock(&context->io_mutex);
102
103	for (i = firstIndex; i < context->table_size; i++) {
104		if (!context->fds[i]) {
105			fd = i;
106			break;
107		}
108	}
109	if (fd < 0) {
110		fd = FSSH_B_NO_MORE_FDS;
111		goto err;
112	}
113
114	context->fds[fd] = descriptor;
115	context->num_used_fds++;
116	fssh_atomic_add(&descriptor->open_count, 1);
117
118err:
119	fssh_mutex_unlock(&context->io_mutex);
120
121	return fd;
122}
123
124
125int
126new_fd(struct io_context *context, struct file_descriptor *descriptor)
127{
128	return new_fd_etc(context, descriptor, 0);
129}
130
131
132/**	Reduces the descriptor's reference counter, and frees all resources
133 *	when it's no longer used.
134 */
135
136void
137put_fd(struct file_descriptor *descriptor)
138{
139	int32_t previous = fssh_atomic_add(&descriptor->ref_count, -1);
140
141	TRACE(("put_fd(descriptor = %p [ref = %ld, cookie = %p])\n",
142		descriptor, descriptor->ref_count, descriptor->cookie));
143
144	// free the descriptor if we don't need it anymore
145	if (previous == 1) {
146		// free the underlying object
147		if (descriptor->ops != NULL && descriptor->ops->fd_free != NULL)
148			descriptor->ops->fd_free(descriptor);
149
150		free(descriptor);
151	} else if ((descriptor->open_mode & FSSH_O_DISCONNECTED) != 0
152		&& previous - 1 == descriptor->open_count
153		&& descriptor->ops != NULL) {
154		// the descriptor has been disconnected - it cannot
155		// be accessed anymore, let's close it (no one is
156		// currently accessing this descriptor)
157
158		if (descriptor->ops->fd_close)
159			descriptor->ops->fd_close(descriptor);
160		if (descriptor->ops->fd_free)
161			descriptor->ops->fd_free(descriptor);
162
163		// prevent this descriptor from being closed/freed again
164		descriptor->open_count = -1;
165		descriptor->ref_count = -1;
166		descriptor->ops = NULL;
167		descriptor->u.vnode = NULL;
168
169		// the file descriptor is kept intact, so that it's not
170		// reused until someone explicetly closes it
171	}
172}
173
174
175/**	Decrements the open counter of the file descriptor and invokes
176 *	its close hook when appropriate.
177 */
178
179void
180close_fd(struct file_descriptor *descriptor)
181{
182	if (fssh_atomic_add(&descriptor->open_count, -1) == 1) {
183		vfs_unlock_vnode_if_locked(descriptor);
184
185		if (descriptor->ops != NULL && descriptor->ops->fd_close != NULL)
186			descriptor->ops->fd_close(descriptor);
187	}
188}
189
190
191/**	This descriptor's underlying object will be closed and freed
192 *	as soon as possible (in one of the next calls to put_fd() -
193 *	get_fd() will no longer succeed on this descriptor).
194 *	This is useful if the underlying object is gone, for instance
195 *	when a (mounted) volume got removed unexpectedly.
196 */
197
198void
199disconnect_fd(struct file_descriptor *descriptor)
200{
201	descriptor->open_mode |= FSSH_O_DISCONNECTED;
202}
203
204
205void
206inc_fd_ref_count(struct file_descriptor *descriptor)
207{
208	fssh_atomic_add(&descriptor->ref_count, 1);
209}
210
211
212struct file_descriptor *
213get_fd(struct io_context *context, int fd)
214{
215	struct file_descriptor *descriptor = NULL;
216
217	if (fd < 0)
218		return NULL;
219
220	fssh_mutex_lock(&context->io_mutex);
221
222	if ((uint32_t)fd < context->table_size)
223		descriptor = context->fds[fd];
224
225	if (descriptor != NULL) {
226		// Disconnected descriptors cannot be accessed anymore
227		if (descriptor->open_mode & FSSH_O_DISCONNECTED)
228			descriptor = NULL;
229		else
230			inc_fd_ref_count(descriptor);
231	}
232
233	fssh_mutex_unlock(&context->io_mutex);
234
235	return descriptor;
236}
237
238
239/**	Removes the file descriptor from the specified slot.
240 */
241
242static struct file_descriptor *
243remove_fd(struct io_context *context, int fd)
244{
245	struct file_descriptor *descriptor = NULL;
246
247	if (fd < 0)
248		return NULL;
249
250	fssh_mutex_lock(&context->io_mutex);
251
252	if ((uint32_t)fd < context->table_size)
253		descriptor = context->fds[fd];
254
255	if (descriptor)	{
256		// fd is valid
257		context->fds[fd] = NULL;
258		fd_set_close_on_exec(context, fd, false);
259		context->num_used_fds--;
260
261		if (descriptor->open_mode & FSSH_O_DISCONNECTED)
262			descriptor = NULL;
263	}
264
265	fssh_mutex_unlock(&context->io_mutex);
266
267	return descriptor;
268}
269
270
271static int
272dup_fd(int fd, bool kernel)
273{
274	struct io_context *context = get_current_io_context(kernel);
275	struct file_descriptor *descriptor;
276	int status;
277
278	TRACE(("dup_fd: fd = %d\n", fd));
279
280	// Try to get the fd structure
281	descriptor = get_fd(context, fd);
282	if (descriptor == NULL)
283		return FSSH_B_FILE_ERROR;
284
285	// now put the fd in place
286	status = new_fd(context, descriptor);
287	if (status < 0)
288		put_fd(descriptor);
289	else {
290		fssh_mutex_lock(&context->io_mutex);
291		fd_set_close_on_exec(context, status, false);
292		fssh_mutex_unlock(&context->io_mutex);
293	}
294
295	return status;
296}
297
298
299/**	POSIX says this should be the same as:
300 *		close(newfd);
301 *		fcntl(oldfd, F_DUPFD, newfd);
302 *
303 *	We do dup2() directly to be thread-safe.
304 */
305
306static int
307dup2_fd(int oldfd, int newfd, bool kernel)
308{
309	struct file_descriptor *evicted = NULL;
310	struct io_context *context;
311
312	TRACE(("dup2_fd: ofd = %d, nfd = %d\n", oldfd, newfd));
313
314	// quick check
315	if (oldfd < 0 || newfd < 0)
316		return FSSH_B_FILE_ERROR;
317
318	// Get current I/O context and lock it
319	context = get_current_io_context(kernel);
320	fssh_mutex_lock(&context->io_mutex);
321
322	// Check if the fds are valid (mutex must be locked because
323	// the table size could be changed)
324	if ((uint32_t)oldfd >= context->table_size
325		|| (uint32_t)newfd >= context->table_size
326		|| context->fds[oldfd] == NULL) {
327		fssh_mutex_unlock(&context->io_mutex);
328		return FSSH_B_FILE_ERROR;
329	}
330
331	// Check for identity, note that it cannot be made above
332	// because we always want to return an error on invalid
333	// handles
334	if (oldfd != newfd) {
335		// Now do the work
336		evicted = context->fds[newfd];
337		fssh_atomic_add(&context->fds[oldfd]->ref_count, 1);
338		fssh_atomic_add(&context->fds[oldfd]->open_count, 1);
339		context->fds[newfd] = context->fds[oldfd];
340
341		if (evicted == NULL)
342			context->num_used_fds++;
343	}
344
345	fd_set_close_on_exec(context, newfd, false);
346
347	fssh_mutex_unlock(&context->io_mutex);
348
349	// Say bye bye to the evicted fd
350	if (evicted) {
351		close_fd(evicted);
352		put_fd(evicted);
353	}
354
355	return newfd;
356}
357
358
359fssh_status_t
360select_fd(int fd, uint8_t event, uint32_t ref, struct select_sync *sync, bool kernel)
361{
362// 	struct file_descriptor *descriptor;
363// 	fssh_status_t status;
364//
365// 	TRACE(("select_fd(fd = %d, event = %u, ref = %lu, selectsync = %p)\n", fd, event, ref, sync));
366//
367// 	descriptor = get_fd(get_current_io_context(kernel), fd);
368// 	if (descriptor == NULL)
369// 		return FSSH_B_FILE_ERROR;
370//
371// 	if (descriptor->ops->fd_select) {
372// 		status = descriptor->ops->fd_select(descriptor, event, ref, sync);
373// 	} else {
374// 		// if the I/O subsystem doesn't support select(), we will
375// 		// immediately notify the select call
376// 		status = notify_select_event((void *)sync, ref, event);
377// 	}
378//
379// 	put_fd(descriptor);
380// 	return status;
381
382	return FSSH_B_BAD_VALUE;
383}
384
385
386fssh_status_t
387deselect_fd(int fd, uint8_t event, struct select_sync *sync, bool kernel)
388{
389// 	struct file_descriptor *descriptor;
390// 	fssh_status_t status;
391//
392// 	TRACE(("deselect_fd(fd = %d, event = %u, selectsync = %p)\n", fd, event, sync));
393//
394// 	descriptor = get_fd(get_current_io_context(kernel), fd);
395// 	if (descriptor == NULL)
396// 		return FSSH_B_FILE_ERROR;
397//
398// 	if (descriptor->ops->fd_deselect)
399// 		status = descriptor->ops->fd_deselect(descriptor, event, sync);
400// 	else
401// 		status = FSSH_B_OK;
402//
403// 	put_fd(descriptor);
404// 	return status;
405
406	return FSSH_B_BAD_VALUE;
407}
408
409
410/** This function checks if the specified fd is valid in the current
411 *	context. It can be used for a quick check; the fd is not locked
412 *	so it could become invalid immediately after this check.
413 */
414
415bool
416fd_is_valid(int fd, bool kernel)
417{
418	struct file_descriptor *descriptor = get_fd(get_current_io_context(kernel), fd);
419	if (descriptor == NULL)
420		return false;
421
422	put_fd(descriptor);
423	return true;
424}
425
426
427struct vnode *
428fd_vnode(struct file_descriptor *descriptor)
429{
430	switch (descriptor->type) {
431		case FDTYPE_FILE:
432		case FDTYPE_DIR:
433		case FDTYPE_ATTR_DIR:
434		case FDTYPE_ATTR:
435			return descriptor->u.vnode;
436	}
437
438	return NULL;
439}
440
441
442static fssh_status_t
443common_close(int fd, bool kernel)
444{
445	struct io_context *io = get_current_io_context(kernel);
446	struct file_descriptor *descriptor = remove_fd(io, fd);
447
448	if (descriptor == NULL)
449		return FSSH_B_FILE_ERROR;
450
451#ifdef TRACE_FD
452	if (!kernel)
453		TRACE(("_user_close(descriptor = %p)\n", descriptor));
454#endif
455
456	close_fd(descriptor);
457	put_fd(descriptor);
458		// the reference associated with the slot
459
460	return FSSH_B_OK;
461}
462
463
464//	#pragma mark -
465//	Kernel calls
466
467
468fssh_ssize_t
469_kern_read(int fd, fssh_off_t pos, void *buffer, fssh_size_t length)
470{
471	struct file_descriptor *descriptor;
472	fssh_ssize_t bytesRead;
473
474	descriptor = get_fd(get_current_io_context(true), fd);
475	if (!descriptor)
476		return FSSH_B_FILE_ERROR;
477	if ((descriptor->open_mode & FSSH_O_RWMASK) == FSSH_O_WRONLY) {
478		put_fd(descriptor);
479		return FSSH_B_FILE_ERROR;
480	}
481
482	if (pos == -1)
483		pos = descriptor->pos;
484
485	if (descriptor->ops->fd_read) {
486		bytesRead = descriptor->ops->fd_read(descriptor, pos, buffer, &length);
487		if (bytesRead >= FSSH_B_OK) {
488			if (length > FSSH_SSIZE_MAX)
489				bytesRead = FSSH_SSIZE_MAX;
490			else
491				bytesRead = (fssh_ssize_t)length;
492
493			descriptor->pos = pos + length;
494		}
495	} else
496		bytesRead = FSSH_B_BAD_VALUE;
497
498	put_fd(descriptor);
499	return bytesRead;
500}
501
502
503fssh_ssize_t
504_kern_readv(int fd, fssh_off_t pos, const fssh_iovec *vecs, fssh_size_t count)
505{
506	struct file_descriptor *descriptor;
507	fssh_ssize_t bytesRead = 0;
508	fssh_status_t status;
509	uint32_t i;
510
511	descriptor = get_fd(get_current_io_context(true), fd);
512	if (!descriptor)
513		return FSSH_B_FILE_ERROR;
514	if ((descriptor->open_mode & FSSH_O_RWMASK) == FSSH_O_WRONLY) {
515		put_fd(descriptor);
516		return FSSH_B_FILE_ERROR;
517	}
518
519	if (pos == -1)
520		pos = descriptor->pos;
521
522	if (descriptor->ops->fd_read) {
523		for (i = 0; i < count; i++) {
524			fssh_size_t length = vecs[i].iov_len;
525			status = descriptor->ops->fd_read(descriptor, pos, vecs[i].iov_base, &length);
526			if (status < FSSH_B_OK) {
527				bytesRead = status;
528				break;
529			}
530
531			if ((uint32_t)bytesRead + length > FSSH_SSIZE_MAX)
532				bytesRead = FSSH_SSIZE_MAX;
533			else
534				bytesRead += (fssh_ssize_t)length;
535
536			pos += vecs[i].iov_len;
537		}
538	} else
539		bytesRead = FSSH_B_BAD_VALUE;
540
541	descriptor->pos = pos;
542	put_fd(descriptor);
543	return bytesRead;
544}
545
546
547fssh_ssize_t
548_kern_write(int fd, fssh_off_t pos, const void *buffer, fssh_size_t length)
549{
550	struct file_descriptor *descriptor;
551	fssh_ssize_t bytesWritten;
552
553	descriptor = get_fd(get_current_io_context(true), fd);
554	if (descriptor == NULL)
555		return FSSH_B_FILE_ERROR;
556	if ((descriptor->open_mode & FSSH_O_RWMASK) == FSSH_O_RDONLY) {
557		put_fd(descriptor);
558		return FSSH_B_FILE_ERROR;
559	}
560
561	if (pos == -1)
562		pos = descriptor->pos;
563
564	if (descriptor->ops->fd_write) {
565		bytesWritten = descriptor->ops->fd_write(descriptor, pos, buffer, &length);
566		if (bytesWritten >= FSSH_B_OK) {
567			if (length > FSSH_SSIZE_MAX)
568				bytesWritten = FSSH_SSIZE_MAX;
569			else
570				bytesWritten = (fssh_ssize_t)length;
571
572			descriptor->pos = pos + length;
573		}
574	} else
575		bytesWritten = FSSH_B_BAD_VALUE;
576
577	put_fd(descriptor);
578	return bytesWritten;
579}
580
581
582fssh_ssize_t
583_kern_writev(int fd, fssh_off_t pos, const fssh_iovec *vecs, fssh_size_t count)
584{
585	struct file_descriptor *descriptor;
586	fssh_ssize_t bytesWritten = 0;
587	fssh_status_t status;
588	uint32_t i;
589
590	descriptor = get_fd(get_current_io_context(true), fd);
591	if (!descriptor)
592		return FSSH_B_FILE_ERROR;
593	if ((descriptor->open_mode & FSSH_O_RWMASK) == FSSH_O_RDONLY) {
594		put_fd(descriptor);
595		return FSSH_B_FILE_ERROR;
596	}
597
598	if (pos == -1)
599		pos = descriptor->pos;
600
601	if (descriptor->ops->fd_write) {
602		for (i = 0; i < count; i++) {
603			fssh_size_t length = vecs[i].iov_len;
604			status = descriptor->ops->fd_write(descriptor, pos, vecs[i].iov_base, &length);
605			if (status < FSSH_B_OK) {
606				bytesWritten = status;
607				break;
608			}
609
610			if ((uint32_t)bytesWritten + length > FSSH_SSIZE_MAX)
611				bytesWritten = FSSH_SSIZE_MAX;
612			else
613				bytesWritten += (fssh_ssize_t)length;
614
615			pos += vecs[i].iov_len;
616		}
617	} else
618		bytesWritten = FSSH_B_BAD_VALUE;
619
620	descriptor->pos = pos;
621	put_fd(descriptor);
622	return bytesWritten;
623}
624
625
626fssh_off_t
627_kern_seek(int fd, fssh_off_t pos, int seekType)
628{
629	struct file_descriptor *descriptor;
630
631	descriptor = get_fd(get_current_io_context(true), fd);
632	if (!descriptor)
633		return FSSH_B_FILE_ERROR;
634
635	if (descriptor->ops->fd_seek)
636		pos = descriptor->ops->fd_seek(descriptor, pos, seekType);
637	else
638		pos = FSSH_ESPIPE;
639
640	put_fd(descriptor);
641	return pos;
642}
643
644
645fssh_status_t
646_kern_ioctl(int fd, uint32_t op, void *buffer, fssh_size_t length)
647{
648	struct file_descriptor *descriptor;
649	int status;
650
651	TRACE(("sys_ioctl: fd %d\n", fd));
652
653	descriptor = get_fd(get_current_io_context(true), fd);
654	if (descriptor == NULL)
655		return FSSH_B_FILE_ERROR;
656
657	if (descriptor->ops->fd_ioctl)
658		status = descriptor->ops->fd_ioctl(descriptor, op, buffer, length);
659	else
660		status = FSSH_EOPNOTSUPP;
661
662	put_fd(descriptor);
663	return status;
664}
665
666
667fssh_ssize_t
668_kern_read_dir(int fd, struct fssh_dirent *buffer, fssh_size_t bufferSize, uint32_t maxCount)
669{
670	struct file_descriptor *descriptor;
671	fssh_ssize_t retval;
672
673	TRACE(("sys_read_dir(fd = %d, buffer = %p, bufferSize = %ld, count = %lu)\n",fd, buffer, bufferSize, maxCount));
674
675	descriptor = get_fd(get_current_io_context(true), fd);
676	if (descriptor == NULL)
677		return FSSH_B_FILE_ERROR;
678
679	if (descriptor->ops->fd_read_dir) {
680		uint32_t count = maxCount;
681		retval = descriptor->ops->fd_read_dir(descriptor, buffer, bufferSize, &count);
682		if (retval >= 0)
683			retval = count;
684	} else
685		retval = FSSH_EOPNOTSUPP;
686
687	put_fd(descriptor);
688	return retval;
689}
690
691
692fssh_status_t
693_kern_rewind_dir(int fd)
694{
695	struct file_descriptor *descriptor;
696	fssh_status_t status;
697
698	TRACE(("sys_rewind_dir(fd = %d)\n",fd));
699
700	descriptor = get_fd(get_current_io_context(true), fd);
701	if (descriptor == NULL)
702		return FSSH_B_FILE_ERROR;
703
704	if (descriptor->ops->fd_rewind_dir)
705		status = descriptor->ops->fd_rewind_dir(descriptor);
706	else
707		status = FSSH_EOPNOTSUPP;
708
709	put_fd(descriptor);
710	return status;
711}
712
713
714fssh_status_t
715_kern_close(int fd)
716{
717	return common_close(fd, true);
718}
719
720
721int
722_kern_dup(int fd)
723{
724	return dup_fd(fd, true);
725}
726
727
728int
729_kern_dup2(int ofd, int nfd)
730{
731	return dup2_fd(ofd, nfd, true);
732}
733
734}	// namespace FSShell
735