file_t __file_name_lookup_at (int fd, int at_flags, const char *file_name, int flags, mode_t mode) { error_t err; file_t result; int empty = at_flags & AT_EMPTY_PATH; at_flags &= ~AT_EMPTY_PATH; err = __hurd_at_flags (&at_flags, &flags); if (err) return (__hurd_fail (err), MACH_PORT_NULL); if (fd == AT_FDCWD || file_name[0] == '/') return __file_name_lookup (file_name, flags, mode); if (empty != 0 && file_name[0] == '\0') { enum retry_type doretry; char retryname[1024]; /* XXX string_t LOSES! */ err = HURD_DPORT_USE (fd, __dir_lookup (port, "", flags, mode, &doretry, retryname, &result)); if (! err) err = __hurd_file_name_lookup_retry (&_hurd_ports_use, &__getdport, NULL, doretry, retryname, flags, mode, &result); return err ? (__hurd_dfail (fd, err), MACH_PORT_NULL) : result; } file_t startdir; error_t use_init_port (int which, error_t (*operate) (mach_port_t)) { return (which == INIT_PORT_CWDIR ? (*operate) (startdir) : _hurd_ports_use (which, operate)); } err = HURD_DPORT_USE (fd, (startdir = port, __hurd_file_name_lookup (&use_init_port, &__getdport, NULL, file_name, flags, mode & ~_hurd_umask, &result))); return err ? (__hurd_dfail (fd, err), MACH_PORT_NULL) : result; }
/* XXX should be __getsockopt ? */ int getsockopt (int fd, int level, int optname, void *optval, socklen_t *optlen) { error_t err; char *buf = optval; mach_msg_type_number_t buflen = *optlen; if (err = HURD_DPORT_USE (fd, __socket_getopt (port, level, optname, &buf, &buflen))) return __hurd_dfail (fd, err); if (*optlen > buflen) *optlen = buflen; if (buf != optval) { memcpy (optval, buf, *optlen); __vm_deallocate (__mach_task_self (), (vm_address_t) buf, buflen); } return 0; }
ssize_t __libc_write (int fd, const void *buf, size_t nbytes) { error_t err = HURD_FD_USE (fd, _hurd_fd_write (descriptor, buf, &nbytes, -1)); return err ? __hurd_dfail (fd, err) : nbytes; }
/* Store at most BUFLEN characters of the pathname of the terminal FD is open on in BUF. Return 0 on success, -1 otherwise. */ int __ttyname_r (int fd, char *buf, size_t buflen) { error_t err; char nodename[1024]; /* XXX */ size_t len; nodename[0] = '\0'; if (err = HURD_DPORT_USE (fd, __term_get_nodename (port, nodename))) { if (err == MIG_BAD_ID || err == EOPNOTSUPP) err = ENOTTY; return __hurd_dfail (fd, err), errno; } len = strlen (nodename) + 1; if (len > buflen) { errno = ERANGE; return errno; } memcpy (buf, nodename, len); return 0; }
file_t __file_name_split_at (int fd, const char *file_name, char **name) { error_t err; file_t result; if (fd == AT_FDCWD || file_name[0] == '/') return __file_name_split (file_name, name); err = __hurd_file_name_split (&_hurd_ports_use, &__getdport, 0, file_name, &result, name); file_t startdir; error_t use_init_port (int which, error_t (*operate) (mach_port_t)) { return (which == INIT_PORT_CWDIR ? (*operate) (startdir) : _hurd_ports_use (which, operate)); } err = HURD_DPORT_USE (fd, (startdir = port, __hurd_file_name_split (&use_init_port, &__getdport, 0, file_name, &result, name))); return err ? (__hurd_dfail (fd, err), MACH_PORT_NULL) : result; }
/* Truncate the file referenced by FD to LENGTH bytes. */ int __ftruncate (int fd, __off_t length) { error_t err; if (err = HURD_DPORT_USE (fd, __file_set_size (port, length))) return __hurd_dfail (fd, err); return 0; }
/* Make all changes done to FD's file data actually appear on disk. */ int fdatasync (int fd) { error_t err = HURD_DPORT_USE (fd, __file_sync (port, 1, 1)); if (err) return __hurd_dfail (fd, err); return 0; }
ssize_t fgetxattr (int fd, const char *name, void *value, size_t size) { error_t err; err = HURD_DPORT_USE (fd, _hurd_xattr_get (port, name, value, &size)); return err ? __hurd_dfail (fd, err) : size; }
ssize_t fsetxattr (int fd, const char *name, const void *value, size_t size, int flags) { error_t err; err = HURD_DPORT_USE (fd, _hurd_xattr_set (port, name, value, size, flags)); return err ? __hurd_dfail (fd, err) : 0; }
ssize_t flistxattr (int fd, char *list, size_t size) { error_t err; err = HURD_DPORT_USE (fd, _hurd_xattr_list (port, list, &size)); return err ? __hurd_dfail (fd, err) : size; }
int fremovexattr (int fd, const char *name) { error_t err; err = HURD_DPORT_USE (fd, _hurd_xattr_remove (port, name)); return err ? __hurd_dfail (fd, err) : 0; }
/* Apply or remove an advisory lock, according to OPERATION, on the file FD refers to. */ int __flock (int fd, int operation) { error_t err; if (err = HURD_DPORT_USE (fd, __file_lock (port, operation))) return __hurd_dfail (fd, err); return 0; }
/* Change the permissions of the file referenced by FD to MODE. */ int __fchmod (int fd, mode_t mode) { error_t err; if (err = HURD_DPORT_USE (fd, __file_chmod (port, mode))) return __hurd_dfail (fd, err); return 0; }
/* Return information about the filesystem on which FD resides. */ int __fstatfs64 (int fd, struct statfs64 *buf) { error_t err; if (err = HURD_DPORT_USE (fd, __file_statfs (port, buf))) return __hurd_dfail (fd, err); return 0; }
/* XXX should be __fchflags? */ int fchflags (int fd, int flags) { error_t err; if (err = HURD_DPORT_USE (fd, __file_chflags (port, flags))) return __hurd_dfail (fd, err); return 0; }
ssize_t __libc_pread64 (int fd, void *buf, size_t nbytes, off64_t offset) { error_t err; if (offset < 0) err = EINVAL; else err = HURD_FD_USE (fd, _hurd_fd_read (descriptor, buf, &nbytes, offset)); return err ? __hurd_dfail (fd, err) : nbytes; }
/* Get file-specific information about descriptor FD. */ long int __fpathconf (int fd, int name) { error_t err; int value; if (err = HURD_DPORT_USE (fd, __io_pathconf (port, name, &value))) return __hurd_dfail (fd, err), -1L; return value; }
/* Return the pathname of the terminal FD is open on, or NULL on errors. The returned storage is good only until the next call to this function. */ char * ttyname (int fd) { error_t err; static char nodename[1024]; /* XXX */ nodename[0] = '\0'; if (err = HURD_DPORT_USE (fd, __term_get_nodename (port, nodename))) return __hurd_dfail (fd, err), NULL; return nodename; }
/* Get information about the file descriptor FD in BUF. */ int __fxstat64 (int vers, int fd, struct stat64 *buf) { error_t err; if (vers != _STAT_VER) return __hurd_fail (EINVAL); if (err = HURD_DPORT_USE (fd, __io_stat (port, buf))) return __hurd_dfail (fd, err); return 0; }
/* Set socket FD's option OPTNAME at protocol level LEVEL to *OPTVAL (which is OPTLEN bytes long). Returns 0 on success, -1 for errors. */ int __setsockopt (int fd, int level, int optname, const void *optval, socklen_t optlen) { error_t err = HURD_DPORT_USE (fd, __socket_setopt (port, level, optname, optval, optlen)); if (err) return __hurd_dfail (fd, err); return 0; }
/* Make all changes done to FD's file data actually appear on disk. */ int fdatasync (int fd) { error_t err = HURD_DPORT_USE (fd, __file_sync (port, 1, 1)); if (err) { if (err == EOPNOTSUPP) /* If the file descriptor does not support sync, return EINVAL as POSIX specifies. */ err = EINVAL; return __hurd_dfail (fd, err); } return 0; }
/* Return the pathname of the terminal FD is open on, or NULL on errors. The returned storage is good only until the next call to this function. */ char * ttyname (int fd) { error_t err; static char nodename[1024]; /* XXX */ nodename[0] = '\0'; if (err = HURD_DPORT_USE (fd, __term_get_nodename (port, nodename))) { if (err == MIG_BAD_ID || err == EOPNOTSUPP) err = ENOTTY; return __hurd_dfail (fd, err), NULL; } return nodename; }
/* Store at most BUFLEN characters of the pathname of the slave pseudo terminal associated with the master FD is open on in BUF. Return 0 on success, otherwise an error number. */ int __ptsname_r (int fd, char *buf, size_t buflen) { string_t peername; size_t len; error_t err; if (err = HURD_DPORT_USE (fd, __term_get_peername (port, peername))) return __hurd_dfail (fd, err), errno; len = __strnlen (peername, sizeof peername - 1) + 1; if (len > buflen) { errno = ERANGE; return ERANGE; } memcpy (buf, peername, len); return 0; }
/* Store at most BUFLEN characters of the pathname of the terminal FD is open on in BUF. Return 0 on success, -1 otherwise. */ int __ttyname_r (int fd, char *buf, size_t buflen) { error_t err; char nodename[1024]; /* XXX */ size_t len; nodename[0] = '\0'; if (err = HURD_DPORT_USE (fd, __term_get_nodename (port, nodename))) return __hurd_dfail (fd, err), -1; len = strlen (nodename) + 1; if (len > buflen) { errno = EINVAL; return -1; } memcpy (buf, nodename, len); return 0; }
file_t __file_name_lookup_at (int fd, int at_flags, const char *file_name, int flags, mode_t mode) { error_t err; file_t result; if ((at_flags & AT_SYMLINK_FOLLOW) && (at_flags & AT_SYMLINK_NOFOLLOW)) return (__hurd_fail (EINVAL), MACH_PORT_NULL); flags |= (at_flags & AT_SYMLINK_NOFOLLOW) ? O_NOLINK : 0; at_flags &= ~AT_SYMLINK_NOFOLLOW; if (at_flags & AT_SYMLINK_FOLLOW) flags &= ~O_NOLINK; at_flags &= ~AT_SYMLINK_FOLLOW; if (at_flags != 0) return (__hurd_fail (EINVAL), MACH_PORT_NULL); if (fd == AT_FDCWD || file_name[0] == '/') return __file_name_lookup (file_name, flags, mode); file_t startdir; error_t use_init_port (int which, error_t (*operate) (mach_port_t)) { return (which == INIT_PORT_CWDIR ? (*operate) (startdir) : _hurd_ports_use (which, operate)); } err = HURD_DPORT_USE (fd, (startdir = port, __hurd_file_name_lookup (&use_init_port, &__getdport, NULL, file_name, flags, mode & ~_hurd_umask, &result))); return err ? (__hurd_dfail (fd, err), MACH_PORT_NULL) : result; }
/* Change the access time of FD to TVP[0] and the modification time of FD to TVP[1]. */ int __futimes (int fd, const struct timeval tvp[2]) { union tv { struct timeval tv; time_value_t tvt; }; const union tv *u = (const union tv *) tvp; union tv nulltv[2]; error_t err; if (tvp == NULL) { /* Setting the number of microseconds to `-1' tells the underlying filesystems to use the current time. */ nulltv[0].tvt.microseconds = nulltv[1].tvt.microseconds = -1; u = nulltv; } err = HURD_DPORT_USE (fd, __file_utimes (port, u[0].tvt, u[1].tvt)); return err ? __hurd_dfail (fd, err) : 0; }
__ptr_t __mmap (__ptr_t addr, size_t len, int prot, int flags, int fd, off_t offset) { error_t err; vm_prot_t vmprot; memory_object_t memobj; vm_address_t mapaddr; vm_size_t pageoff; mapaddr = (vm_address_t) addr; if ((flags & (MAP_TYPE|MAP_INHERIT)) == MAP_ANON && prot == (PROT_READ|PROT_WRITE)) /* cf VM_PROT_DEFAULT */ { /* vm_allocate has (a little) less overhead in the kernel too. */ err = __vm_allocate (__mach_task_self (), &mapaddr, len, !(flags & MAP_FIXED)); if (err == KERN_NO_SPACE && (flags & MAP_FIXED)) { /* XXX this is not atomic as it is in unix! */ /* The region is already allocated; deallocate it first. */ err = __vm_deallocate (__mach_task_self (), mapaddr, len); if (!err) err = __vm_allocate (__mach_task_self (), &mapaddr, len, 0); } return err ? (__ptr_t) (long int) __hurd_fail (err) : (__ptr_t) mapaddr; } pageoff = offset & (vm_page_size - 1); offset &= ~(vm_page_size - 1); if (flags & MAP_FIXED) { /* A specific address is requested. It need not be page-aligned; it just needs to be congruent with the object offset. */ if ((mapaddr & (vm_page_size - 1)) != pageoff) return (__ptr_t) (long int) __hurd_fail (EINVAL); else /* We will add back PAGEOFF after mapping. */ mapaddr -= pageoff; } vmprot = VM_PROT_NONE; if (prot & PROT_READ) vmprot |= VM_PROT_READ; if (prot & PROT_WRITE) vmprot |= VM_PROT_WRITE; if (prot & PROT_EXEC) vmprot |= VM_PROT_EXECUTE; switch (flags & MAP_TYPE) { default: return (__ptr_t) (long int) __hurd_fail (EINVAL); case MAP_ANON: memobj = MACH_PORT_NULL; break; case MAP_FILE: case 0: /* Allow, e.g., just MAP_SHARED. */ { mach_port_t robj, wobj; if (err = HURD_DPORT_USE (fd, __io_map (port, &robj, &wobj))) return (__ptr_t) (long int) __hurd_dfail (fd, err); switch (prot & (PROT_READ|PROT_WRITE)) { case PROT_READ: memobj = robj; if (wobj != MACH_PORT_NULL) __mach_port_deallocate (__mach_task_self (), wobj); break; case PROT_WRITE: memobj = wobj; if (robj != MACH_PORT_NULL) __mach_port_deallocate (__mach_task_self (), robj); break; case PROT_READ|PROT_WRITE: if (robj == wobj) { memobj = wobj; /* Remove extra reference. */ __mach_port_deallocate (__mach_task_self (), memobj); } else if (wobj == MACH_PORT_NULL && /* Not writable by mapping. */ !(flags & MAP_SHARED)) /* The file can only be mapped for reading. Since we are making a private mapping, we will never try to write the object anyway, so we don't care. */ memobj = robj; else { __mach_port_deallocate (__mach_task_self (), wobj); return (__ptr_t) (long int) __hurd_fail (EACCES); } break; } break; /* XXX handle MAP_NOEXTEND */ } } /* XXX handle MAP_INHERIT */ err = __vm_map (__mach_task_self (), &mapaddr, (vm_size_t) len, (vm_address_t) 0, ! (flags & MAP_FIXED), memobj, (vm_offset_t) offset, ! (flags & MAP_SHARED), vmprot, VM_PROT_ALL, (flags & MAP_SHARED) ? VM_INHERIT_SHARE : VM_INHERIT_COPY); if (err == KERN_NO_SPACE && (flags & MAP_FIXED)) { /* XXX this is not atomic as it is in unix! */ /* The region is already allocated; deallocate it first. */ err = __vm_deallocate (__mach_task_self (), mapaddr, len); if (! err) err = __vm_map (__mach_task_self (), &mapaddr, (vm_size_t) len, (vm_address_t) 0, 0, memobj, (vm_offset_t) offset, ! (flags & MAP_SHARED), vmprot, VM_PROT_ALL, (flags & MAP_SHARED) ? VM_INHERIT_SHARE : VM_INHERIT_COPY); } if (memobj != MACH_PORT_NULL) __mach_port_deallocate (__mach_task_self (), memobj); if (err) return (__ptr_t) (long int) __hurd_fail (err); /* Adjust the mapping address for the offset-within-page. */ mapaddr += pageoff; return (__ptr_t) mapaddr; }
__ptr_t __mmap (__ptr_t addr, size_t len, int prot, int flags, int fd, off_t offset) { error_t err; vm_prot_t vmprot; memory_object_t memobj; vm_address_t mapaddr; mapaddr = (vm_address_t) addr; /* ADDR and OFFSET must be page-aligned. */ if ((mapaddr & (__vm_page_size - 1)) || (offset & (__vm_page_size - 1))) return (__ptr_t) (long int) __hurd_fail (EINVAL); if ((flags & (MAP_TYPE|MAP_INHERIT)) == MAP_ANON && prot == (PROT_READ|PROT_WRITE)) /* cf VM_PROT_DEFAULT */ { /* vm_allocate has (a little) less overhead in the kernel too. */ err = __vm_allocate (__mach_task_self (), &mapaddr, len, mapaddr == 0); if (err == KERN_NO_SPACE) { if (flags & MAP_FIXED) { /* XXX this is not atomic as it is in unix! */ /* The region is already allocated; deallocate it first. */ err = __vm_deallocate (__mach_task_self (), mapaddr, len); if (!err) err = __vm_allocate (__mach_task_self (), &mapaddr, len, 0); } else if (mapaddr != 0) err = __vm_allocate (__mach_task_self (), &mapaddr, len, 1); } return err ? (__ptr_t) (long int) __hurd_fail (err) : (__ptr_t) mapaddr; } vmprot = VM_PROT_NONE; if (prot & PROT_READ) vmprot |= VM_PROT_READ; if (prot & PROT_WRITE) vmprot |= VM_PROT_WRITE; if (prot & PROT_EXEC) vmprot |= VM_PROT_EXECUTE; switch (flags & MAP_TYPE) { default: return (__ptr_t) (long int) __hurd_fail (EINVAL); case MAP_ANON: memobj = MACH_PORT_NULL; break; case MAP_FILE: case 0: /* Allow, e.g., just MAP_SHARED. */ { mach_port_t robj, wobj; if (err = HURD_DPORT_USE (fd, __io_map (port, &robj, &wobj))) { if (err == MIG_BAD_ID || err == EOPNOTSUPP || err == ENOSYS) err = ENODEV; /* File descriptor doesn't support mmap. */ return (__ptr_t) (long int) __hurd_dfail (fd, err); } switch (prot & (PROT_READ|PROT_WRITE)) { /* Although it apparently doesn't make sense to map a file with protection set to PROT_NONE, it is actually sometimes done. In particular, that's how localedef reserves some space for the locale archive file, the rationale being that some implementations take into account whether the mapping is anonymous or not when selecting addresses. */ case PROT_NONE: case PROT_READ: memobj = robj; if (wobj != MACH_PORT_NULL) __mach_port_deallocate (__mach_task_self (), wobj); break; case PROT_WRITE: memobj = wobj; if (robj != MACH_PORT_NULL) __mach_port_deallocate (__mach_task_self (), robj); break; case PROT_READ|PROT_WRITE: if (robj == wobj) { memobj = wobj; /* Remove extra reference. */ __mach_port_deallocate (__mach_task_self (), memobj); } else if (wobj == MACH_PORT_NULL && /* Not writable by mapping. */ !(flags & MAP_SHARED)) /* The file can only be mapped for reading. Since we are making a private mapping, we will never try to write the object anyway, so we don't care. */ memobj = robj; else { __mach_port_deallocate (__mach_task_self (), wobj); return (__ptr_t) (long int) __hurd_fail (EACCES); } break; default: __builtin_unreachable (); } break; /* XXX handle MAP_NOEXTEND */ } } /* XXX handle MAP_INHERIT */ err = __vm_map (__mach_task_self (), &mapaddr, (vm_size_t) len, (vm_address_t) 0, mapaddr == 0, memobj, (vm_offset_t) offset, ! (flags & MAP_SHARED), vmprot, VM_PROT_ALL, (flags & MAP_SHARED) ? VM_INHERIT_SHARE : VM_INHERIT_COPY); if (err == KERN_NO_SPACE) { if (flags & MAP_FIXED) { /* XXX this is not atomic as it is in unix! */ /* The region is already allocated; deallocate it first. */ err = __vm_deallocate (__mach_task_self (), mapaddr, len); if (! err) err = __vm_map (__mach_task_self (), &mapaddr, (vm_size_t) len, (vm_address_t) 0, 0, memobj, (vm_offset_t) offset, ! (flags & MAP_SHARED), vmprot, VM_PROT_ALL, (flags & MAP_SHARED) ? VM_INHERIT_SHARE : VM_INHERIT_COPY); } else if (mapaddr != 0) err = __vm_map (__mach_task_self (), &mapaddr, (vm_size_t) len, (vm_address_t) 0, 1, memobj, (vm_offset_t) offset, ! (flags & MAP_SHARED), vmprot, VM_PROT_ALL, (flags & MAP_SHARED) ? VM_INHERIT_SHARE : VM_INHERIT_COPY); } if (memobj != MACH_PORT_NULL) __mach_port_deallocate (__mach_task_self (), memobj); if (err) return (__ptr_t) (long int) __hurd_fail (err); return (__ptr_t) mapaddr; }