xref: /haiku/src/tools/fs_shell/fd.cpp (revision e81a954787e50e56a7f06f72705b7859b6ab06d1)
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 
29 namespace FSShell {
30 
31 
32 io_context* gKernelIOContext;
33 
34 
35 /*** General fd routines ***/
36 
37 
38 #ifdef DEBUG
39 void dump_fd(int fd, struct file_descriptor *descriptor);
40 
41 void
42 dump_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 = %Ld\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 
53 struct file_descriptor *
54 alloc_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 
73 bool
74 fd_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 
80 void
81 fd_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 
94 int
95 new_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 
118 err:
119 	fssh_mutex_unlock(&context->io_mutex);
120 
121 	return fd;
122 }
123 
124 
125 int
126 new_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 
136 void
137 put_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 
179 void
180 close_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 
198 void
199 disconnect_fd(struct file_descriptor *descriptor)
200 {
201 	descriptor->open_mode |= FSSH_O_DISCONNECTED;
202 }
203 
204 
205 void
206 inc_fd_ref_count(struct file_descriptor *descriptor)
207 {
208 	fssh_atomic_add(&descriptor->ref_count, 1);
209 }
210 
211 
212 struct file_descriptor *
213 get_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 
242 static struct file_descriptor *
243 remove_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 
271 static int
272 dup_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 
306 static int
307 dup2_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 
359 fssh_status_t
360 select_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 
386 fssh_status_t
387 deselect_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 
415 bool
416 fd_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 
427 struct vnode *
428 fd_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 
442 static fssh_status_t
443 common_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 
468 fssh_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 
503 fssh_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 
547 fssh_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 
582 fssh_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 
626 fssh_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 
645 fssh_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 
667 fssh_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 
692 fssh_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 
714 fssh_status_t
715 _kern_close(int fd)
716 {
717 	return common_close(fd, true);
718 }
719 
720 
721 int
722 _kern_dup(int fd)
723 {
724 	return dup_fd(fd, true);
725 }
726 
727 
728 int
729 _kern_dup2(int ofd, int nfd)
730 {
731 	return dup2_fd(ofd, nfd, true);
732 }
733 
734 }	// namespace FSShell
735