error_t _hurd_fd_close (struct hurd_fd *fd) { error_t err; HURD_CRITICAL_BEGIN; __spin_lock (&fd->port.lock); if (fd->port.port == MACH_PORT_NULL) { __spin_unlock (&fd->port.lock); err = EBADF; } else { /* Clear the descriptor's port cells. This deallocates the ports if noone else is still using them. */ _hurd_port_set (&fd->ctty, MACH_PORT_NULL); _hurd_port_locked_set (&fd->port, MACH_PORT_NULL); err = 0; } HURD_CRITICAL_END; return err; }
void _hurd_port2fd (struct hurd_fd *d, io_t port, int flags) { io_t ctty; mach_port_t cttyid; int is_ctty = !(flags & O_IGNORE_CTTY) && ! __term_getctty (port, &cttyid); if (is_ctty) { /* This port is capable of being a controlling tty. Is it ours? */ struct hurd_port *const id = &_hurd_ports[INIT_PORT_CTTYID]; __spin_lock (&id->lock); if (id->port == MACH_PORT_NULL) /* We have no controlling tty, so make this one it. */ _hurd_port_locked_set (id, cttyid); else { if (cttyid != id->port) /* We have a controlling tty and this is not it. */ is_ctty = 0; /* Either we don't want CTTYID, or ID->port already is it. So we don't need to change ID->port, and we can release the reference to CTTYID. */ __spin_unlock (&id->lock); __mach_port_deallocate (__mach_task_self (), cttyid); } } if (!is_ctty || __term_open_ctty (port, _hurd_pid, _hurd_pgrp, &ctty) != 0) /* XXX if IS_CTTY, then this port is our ctty, but we are not doing ctty style i/o because term_become_ctty barfed. What to do? */ /* No ctty magic happening here. */ ctty = MACH_PORT_NULL; /* Install PORT in the descriptor cell, leaving it locked. */ { mach_port_t old = _hurd_userlink_clear (&d->port.users) ? d->port.port : MACH_PORT_NULL; d->port.port = port; if (old != MACH_PORT_NULL) __mach_port_deallocate (__mach_task_self (), old); } _hurd_port_set (&d->ctty, ctty); }
/* Duplicate FD to FD2, closing the old FD2 and making FD2 be open on the same file as FD is, and setting FD2's flags according to FLAGS. Return FD2 or -1. */ int __dup3 (int fd, int fd2, int flags) { struct hurd_fd *d; /* Both passing flags different from O_CLOEXEC and FD2 being the same as FD are invalid. */ if ((flags & ~O_CLOEXEC || fd2 == fd) /* ... with the exception in case that dup2 behavior is requested: if FD is valid and FD2 is already the same then just return it. */ && ! (flags == -1 && fd2 == fd)) return __hurd_fail (EINVAL); /* Extract the ports and flags from FD. */ d = _hurd_fd_get (fd); if (d == NULL) return __hurd_fail (EBADF); HURD_CRITICAL_BEGIN; __spin_lock (&d->port.lock); if (d->port.port == MACH_PORT_NULL) { __spin_unlock (&d->port.lock); fd2 = __hurd_fail (EBADF); } else if (fd2 == fd) __spin_unlock (&d->port.lock); else { struct hurd_userlink ulink, ctty_ulink; int d_flags = d->flags; io_t ctty = _hurd_port_get (&d->ctty, &ctty_ulink); io_t port = _hurd_port_locked_get (&d->port, &ulink); /* Unlocks D. */ if (fd2 < 0) fd2 = __hurd_fail (EBADF); else { /* Get a hold of the destination descriptor. */ struct hurd_fd *d2; __mutex_lock (&_hurd_dtable_lock); if (fd2 >= _hurd_dtablesize) { /* The table is not large enough to hold the destination descriptor. Enlarge it as necessary to allocate this descriptor. */ __mutex_unlock (&_hurd_dtable_lock); d2 = _hurd_alloc_fd (NULL, fd2); if (d2) __spin_unlock (&d2->port.lock); __mutex_lock (&_hurd_dtable_lock); } else { d2 = _hurd_dtable[fd2]; if (d2 == NULL) { /* Must allocate a new one. We don't initialize the port cells with this call so that if it fails (out of memory), we will not have already added user references for the ports, which we would then have to deallocate. */ d2 = _hurd_dtable[fd2] = _hurd_new_fd (MACH_PORT_NULL, MACH_PORT_NULL); } } __mutex_unlock (&_hurd_dtable_lock); if (d2 == NULL) { fd2 = -1; if (errno == EINVAL) errno = EBADF; /* POSIX.1-1990 6.2.1.2 ll 54-55. */ } else { /* Give the ports each a user ref for the new descriptor. */ __mach_port_mod_refs (__mach_task_self (), port, MACH_PORT_RIGHT_SEND, 1); if (ctty != MACH_PORT_NULL) __mach_port_mod_refs (__mach_task_self (), ctty, MACH_PORT_RIGHT_SEND, 1); /* Install the ports and flags in the new descriptor slot. */ __spin_lock (&d2->port.lock); if (flags & O_CLOEXEC) d2->flags = d_flags | FD_CLOEXEC; else /* dup clears FD_CLOEXEC. */ d2->flags = d_flags & ~FD_CLOEXEC; _hurd_port_set (&d2->ctty, ctty); _hurd_port_locked_set (&d2->port, port); /* Unlocks D2. */ } } _hurd_port_free (&d->port, &ulink, port); if (ctty != MACH_PORT_NULL) _hurd_port_free (&d->ctty, &ctty_ulink, port); } HURD_CRITICAL_END; return fd2; }