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
dump_fd(int fd,struct file_descriptor * descriptor)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 = %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
53 struct file_descriptor *
alloc_fd(void)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
fd_close_on_exec(struct io_context * context,int fd)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
fd_set_close_on_exec(struct io_context * context,int fd,bool closeFD)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
new_fd_etc(struct io_context * context,struct file_descriptor * descriptor,int firstIndex)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
new_fd(struct io_context * context,struct file_descriptor * descriptor)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
put_fd(struct file_descriptor * descriptor)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
close_fd(struct file_descriptor * descriptor)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
disconnect_fd(struct file_descriptor * descriptor)199 disconnect_fd(struct file_descriptor *descriptor)
200 {
201 descriptor->open_mode |= FSSH_O_DISCONNECTED;
202 }
203
204
205 void
inc_fd_ref_count(struct file_descriptor * descriptor)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 *
get_fd(struct io_context * context,int fd)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 *
remove_fd(struct io_context * context,int fd)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
dup_fd(int fd,bool kernel)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
dup2_fd(int oldfd,int newfd,bool kernel)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
select_fd(int fd,uint8_t event,uint32_t ref,struct select_sync * sync,bool kernel)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
deselect_fd(int fd,uint8_t event,struct select_sync * sync,bool kernel)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
fd_is_valid(int fd,bool kernel)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 *
fd_vnode(struct file_descriptor * descriptor)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
common_close(int fd,bool kernel)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
_kern_read(int fd,fssh_off_t pos,void * buffer,fssh_size_t length)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
_kern_readv(int fd,fssh_off_t pos,const fssh_iovec * vecs,fssh_size_t count)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
_kern_write(int fd,fssh_off_t pos,const void * buffer,fssh_size_t length)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
_kern_writev(int fd,fssh_off_t pos,const fssh_iovec * vecs,fssh_size_t count)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
_kern_seek(int fd,fssh_off_t pos,int seekType)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
_kern_ioctl(int fd,uint32_t op,void * buffer,fssh_size_t length)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
_kern_read_dir(int fd,struct fssh_dirent * buffer,fssh_size_t bufferSize,uint32_t maxCount)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
_kern_rewind_dir(int fd)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
_kern_close(int fd)715 _kern_close(int fd)
716 {
717 return common_close(fd, true);
718 }
719
720
721 int
_kern_dup(int fd)722 _kern_dup(int fd)
723 {
724 return dup_fd(fd, true);
725 }
726
727
728 int
_kern_dup2(int ofd,int nfd)729 _kern_dup2(int ofd, int nfd)
730 {
731 return dup2_fd(ofd, nfd, true);
732 }
733
734 } // namespace FSShell
735