2 * Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
6 //! Operations on file descriptors
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"
23 # define TRACE(x) dprintf x
32 io_context
* gKernelIOContext
;
35 /*** General fd routines ***/
39 void dump_fd(int fd
, struct file_descriptor
*descriptor
);
42 dump_fd(int fd
,struct file_descriptor
*descriptor
)
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
);
51 /** Allocates and initializes a new file_descriptor */
53 struct file_descriptor
*
56 struct file_descriptor
*descriptor
;
58 descriptor
= (file_descriptor
*)malloc(sizeof(struct file_descriptor
));
59 if (descriptor
== NULL
)
62 descriptor
->u
.vnode
= NULL
;
63 descriptor
->cookie
= NULL
;
64 descriptor
->ref_count
= 1;
65 descriptor
->open_count
= 0;
66 descriptor
->open_mode
= 0;
74 fd_close_on_exec(struct io_context
*context
, int fd
)
76 return CHECK_BIT(context
->fds_close_on_exec
[fd
/ 8], fd
& 7) ? true : false;
81 fd_set_close_on_exec(struct io_context
*context
, int fd
, bool closeFD
)
84 context
->fds_close_on_exec
[fd
/ 8] |= (1 << (fd
& 7));
86 context
->fds_close_on_exec
[fd
/ 8] &= ~(1 << (fd
& 7));
90 /** Searches a free slot in the FD table of the provided I/O context, and inserts
91 * the specified descriptor into it.
95 new_fd_etc(struct io_context
*context
, struct file_descriptor
*descriptor
,
101 fssh_mutex_lock(&context
->io_mutex
);
103 for (i
= firstIndex
; i
< context
->table_size
; i
++) {
104 if (!context
->fds
[i
]) {
110 fd
= FSSH_B_NO_MORE_FDS
;
114 context
->fds
[fd
] = descriptor
;
115 context
->num_used_fds
++;
116 fssh_atomic_add(&descriptor
->open_count
, 1);
119 fssh_mutex_unlock(&context
->io_mutex
);
126 new_fd(struct io_context
*context
, struct file_descriptor
*descriptor
)
128 return new_fd_etc(context
, descriptor
, 0);
132 /** Reduces the descriptor's reference counter, and frees all resources
133 * when it's no longer used.
137 put_fd(struct file_descriptor
*descriptor
)
139 int32_t previous
= fssh_atomic_add(&descriptor
->ref_count
, -1);
141 TRACE(("put_fd(descriptor = %p [ref = %ld, cookie = %p])\n",
142 descriptor
, descriptor
->ref_count
, descriptor
->cookie
));
144 // free the descriptor if we don't need it anymore
146 // free the underlying object
147 if (descriptor
->ops
!= NULL
&& descriptor
->ops
->fd_free
!= NULL
)
148 descriptor
->ops
->fd_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)
158 if (descriptor
->ops
->fd_close
)
159 descriptor
->ops
->fd_close(descriptor
);
160 if (descriptor
->ops
->fd_free
)
161 descriptor
->ops
->fd_free(descriptor
);
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
;
169 // the file descriptor is kept intact, so that it's not
170 // reused until someone explicetly closes it
175 /** Decrements the open counter of the file descriptor and invokes
176 * its close hook when appropriate.
180 close_fd(struct file_descriptor
*descriptor
)
182 if (fssh_atomic_add(&descriptor
->open_count
, -1) == 1) {
183 vfs_unlock_vnode_if_locked(descriptor
);
185 if (descriptor
->ops
!= NULL
&& descriptor
->ops
->fd_close
!= NULL
)
186 descriptor
->ops
->fd_close(descriptor
);
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.
199 disconnect_fd(struct file_descriptor
*descriptor
)
201 descriptor
->open_mode
|= FSSH_O_DISCONNECTED
;
206 inc_fd_ref_count(struct file_descriptor
*descriptor
)
208 fssh_atomic_add(&descriptor
->ref_count
, 1);
212 struct file_descriptor
*
213 get_fd(struct io_context
*context
, int fd
)
215 struct file_descriptor
*descriptor
= NULL
;
220 fssh_mutex_lock(&context
->io_mutex
);
222 if ((uint32_t)fd
< context
->table_size
)
223 descriptor
= context
->fds
[fd
];
225 if (descriptor
!= NULL
) {
226 // Disconnected descriptors cannot be accessed anymore
227 if (descriptor
->open_mode
& FSSH_O_DISCONNECTED
)
230 inc_fd_ref_count(descriptor
);
233 fssh_mutex_unlock(&context
->io_mutex
);
239 /** Removes the file descriptor from the specified slot.
242 static struct file_descriptor
*
243 remove_fd(struct io_context
*context
, int fd
)
245 struct file_descriptor
*descriptor
= NULL
;
250 fssh_mutex_lock(&context
->io_mutex
);
252 if ((uint32_t)fd
< context
->table_size
)
253 descriptor
= context
->fds
[fd
];
257 context
->fds
[fd
] = NULL
;
258 fd_set_close_on_exec(context
, fd
, false);
259 context
->num_used_fds
--;
261 if (descriptor
->open_mode
& FSSH_O_DISCONNECTED
)
265 fssh_mutex_unlock(&context
->io_mutex
);
272 dup_fd(int fd
, bool kernel
)
274 struct io_context
*context
= get_current_io_context(kernel
);
275 struct file_descriptor
*descriptor
;
278 TRACE(("dup_fd: fd = %d\n", fd
));
280 // Try to get the fd structure
281 descriptor
= get_fd(context
, fd
);
282 if (descriptor
== NULL
)
283 return FSSH_B_FILE_ERROR
;
285 // now put the fd in place
286 status
= new_fd(context
, descriptor
);
290 fssh_mutex_lock(&context
->io_mutex
);
291 fd_set_close_on_exec(context
, status
, false);
292 fssh_mutex_unlock(&context
->io_mutex
);
299 /** POSIX says this should be the same as:
301 * fcntl(oldfd, F_DUPFD, newfd);
303 * We do dup2() directly to be thread-safe.
307 dup2_fd(int oldfd
, int newfd
, bool kernel
)
309 struct file_descriptor
*evicted
= NULL
;
310 struct io_context
*context
;
312 TRACE(("dup2_fd: ofd = %d, nfd = %d\n", oldfd
, newfd
));
315 if (oldfd
< 0 || newfd
< 0)
316 return FSSH_B_FILE_ERROR
;
318 // Get current I/O context and lock it
319 context
= get_current_io_context(kernel
);
320 fssh_mutex_lock(&context
->io_mutex
);
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
;
331 // Check for identity, note that it cannot be made above
332 // because we always want to return an error on invalid
334 if (oldfd
!= newfd
) {
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
];
342 context
->num_used_fds
++;
345 fd_set_close_on_exec(context
, newfd
, false);
347 fssh_mutex_unlock(&context
->io_mutex
);
349 // Say bye bye to the evicted fd
360 select_fd(int fd
, uint8_t event
, uint32_t ref
, struct select_sync
*sync
, bool kernel
)
362 // struct file_descriptor *descriptor;
363 // fssh_status_t status;
365 // TRACE(("select_fd(fd = %d, event = %u, ref = %lu, selectsync = %p)\n", fd, event, ref, sync));
367 // descriptor = get_fd(get_current_io_context(kernel), fd);
368 // if (descriptor == NULL)
369 // return FSSH_B_FILE_ERROR;
371 // if (descriptor->ops->fd_select) {
372 // status = descriptor->ops->fd_select(descriptor, event, ref, sync);
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);
379 // put_fd(descriptor);
382 return FSSH_B_BAD_VALUE
;
387 deselect_fd(int fd
, uint8_t event
, struct select_sync
*sync
, bool kernel
)
389 // struct file_descriptor *descriptor;
390 // fssh_status_t status;
392 // TRACE(("deselect_fd(fd = %d, event = %u, selectsync = %p)\n", fd, event, sync));
394 // descriptor = get_fd(get_current_io_context(kernel), fd);
395 // if (descriptor == NULL)
396 // return FSSH_B_FILE_ERROR;
398 // if (descriptor->ops->fd_deselect)
399 // status = descriptor->ops->fd_deselect(descriptor, event, sync);
401 // status = FSSH_B_OK;
403 // put_fd(descriptor);
406 return FSSH_B_BAD_VALUE
;
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.
416 fd_is_valid(int fd
, bool kernel
)
418 struct file_descriptor
*descriptor
= get_fd(get_current_io_context(kernel
), fd
);
419 if (descriptor
== NULL
)
428 fd_vnode(struct file_descriptor
*descriptor
)
430 switch (descriptor
->type
) {
433 case FDTYPE_ATTR_DIR
:
435 return descriptor
->u
.vnode
;
443 common_close(int fd
, bool kernel
)
445 struct io_context
*io
= get_current_io_context(kernel
);
446 struct file_descriptor
*descriptor
= remove_fd(io
, fd
);
448 if (descriptor
== NULL
)
449 return FSSH_B_FILE_ERROR
;
453 TRACE(("_user_close(descriptor = %p)\n", descriptor
));
456 close_fd(descriptor
);
458 // the reference associated with the slot
469 _kern_read(int fd
, fssh_off_t pos
, void *buffer
, fssh_size_t length
)
471 struct file_descriptor
*descriptor
;
472 fssh_ssize_t bytesRead
;
474 descriptor
= get_fd(get_current_io_context(true), fd
);
476 return FSSH_B_FILE_ERROR
;
477 if ((descriptor
->open_mode
& FSSH_O_RWMASK
) == FSSH_O_WRONLY
) {
479 return FSSH_B_FILE_ERROR
;
483 pos
= descriptor
->pos
;
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
;
491 bytesRead
= (fssh_ssize_t
)length
;
493 descriptor
->pos
= pos
+ length
;
496 bytesRead
= FSSH_B_BAD_VALUE
;
504 _kern_readv(int fd
, fssh_off_t pos
, const fssh_iovec
*vecs
, fssh_size_t count
)
506 struct file_descriptor
*descriptor
;
507 fssh_ssize_t bytesRead
= 0;
508 fssh_status_t status
;
511 descriptor
= get_fd(get_current_io_context(true), fd
);
513 return FSSH_B_FILE_ERROR
;
514 if ((descriptor
->open_mode
& FSSH_O_RWMASK
) == FSSH_O_WRONLY
) {
516 return FSSH_B_FILE_ERROR
;
520 pos
= descriptor
->pos
;
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
) {
531 if ((uint32_t)bytesRead
+ length
> FSSH_SSIZE_MAX
)
532 bytesRead
= FSSH_SSIZE_MAX
;
534 bytesRead
+= (fssh_ssize_t
)length
;
536 pos
+= vecs
[i
].iov_len
;
539 bytesRead
= FSSH_B_BAD_VALUE
;
541 descriptor
->pos
= pos
;
548 _kern_write(int fd
, fssh_off_t pos
, const void *buffer
, fssh_size_t length
)
550 struct file_descriptor
*descriptor
;
551 fssh_ssize_t bytesWritten
;
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
) {
558 return FSSH_B_FILE_ERROR
;
562 pos
= descriptor
->pos
;
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
;
570 bytesWritten
= (fssh_ssize_t
)length
;
572 descriptor
->pos
= pos
+ length
;
575 bytesWritten
= FSSH_B_BAD_VALUE
;
583 _kern_writev(int fd
, fssh_off_t pos
, const fssh_iovec
*vecs
, fssh_size_t count
)
585 struct file_descriptor
*descriptor
;
586 fssh_ssize_t bytesWritten
= 0;
587 fssh_status_t status
;
590 descriptor
= get_fd(get_current_io_context(true), fd
);
592 return FSSH_B_FILE_ERROR
;
593 if ((descriptor
->open_mode
& FSSH_O_RWMASK
) == FSSH_O_RDONLY
) {
595 return FSSH_B_FILE_ERROR
;
599 pos
= descriptor
->pos
;
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
;
610 if ((uint32_t)bytesWritten
+ length
> FSSH_SSIZE_MAX
)
611 bytesWritten
= FSSH_SSIZE_MAX
;
613 bytesWritten
+= (fssh_ssize_t
)length
;
615 pos
+= vecs
[i
].iov_len
;
618 bytesWritten
= FSSH_B_BAD_VALUE
;
620 descriptor
->pos
= pos
;
627 _kern_seek(int fd
, fssh_off_t pos
, int seekType
)
629 struct file_descriptor
*descriptor
;
631 descriptor
= get_fd(get_current_io_context(true), fd
);
633 return FSSH_B_FILE_ERROR
;
635 if (descriptor
->ops
->fd_seek
)
636 pos
= descriptor
->ops
->fd_seek(descriptor
, pos
, seekType
);
646 _kern_ioctl(int fd
, uint32_t op
, void *buffer
, fssh_size_t length
)
648 struct file_descriptor
*descriptor
;
651 TRACE(("sys_ioctl: fd %d\n", fd
));
653 descriptor
= get_fd(get_current_io_context(true), fd
);
654 if (descriptor
== NULL
)
655 return FSSH_B_FILE_ERROR
;
657 if (descriptor
->ops
->fd_ioctl
)
658 status
= descriptor
->ops
->fd_ioctl(descriptor
, op
, buffer
, length
);
660 status
= FSSH_EOPNOTSUPP
;
668 _kern_read_dir(int fd
, struct fssh_dirent
*buffer
, fssh_size_t bufferSize
, uint32_t maxCount
)
670 struct file_descriptor
*descriptor
;
673 TRACE(("sys_read_dir(fd = %d, buffer = %p, bufferSize = %ld, count = %lu)\n",fd
, buffer
, bufferSize
, maxCount
));
675 descriptor
= get_fd(get_current_io_context(true), fd
);
676 if (descriptor
== NULL
)
677 return FSSH_B_FILE_ERROR
;
679 if (descriptor
->ops
->fd_read_dir
) {
680 uint32_t count
= maxCount
;
681 retval
= descriptor
->ops
->fd_read_dir(descriptor
, buffer
, bufferSize
, &count
);
685 retval
= FSSH_EOPNOTSUPP
;
693 _kern_rewind_dir(int fd
)
695 struct file_descriptor
*descriptor
;
696 fssh_status_t status
;
698 TRACE(("sys_rewind_dir(fd = %d)\n",fd
));
700 descriptor
= get_fd(get_current_io_context(true), fd
);
701 if (descriptor
== NULL
)
702 return FSSH_B_FILE_ERROR
;
704 if (descriptor
->ops
->fd_rewind_dir
)
705 status
= descriptor
->ops
->fd_rewind_dir(descriptor
);
707 status
= FSSH_EOPNOTSUPP
;
717 return common_close(fd
, true);
724 return dup_fd(fd
, true);
729 _kern_dup2(int ofd
, int nfd
)
731 return dup2_fd(ofd
, nfd
, true);
734 } // namespace FSShell