_IO_FILE * _IO_file_open (_IO_FILE *fp, const char *filename, int posix_mode, int prot, int read_write, int is32not64) { int fdesc; #ifdef _LIBC if (__glibc_unlikely (fp->_flags2 & _IO_FLAGS2_NOTCANCEL)) fdesc = open_not_cancel (filename, posix_mode | (is32not64 ? 0 : O_LARGEFILE), prot); else fdesc = open (filename, posix_mode | (is32not64 ? 0 : O_LARGEFILE), prot); #else fdesc = open (filename, posix_mode, prot); #endif if (fdesc < 0) return NULL; fp->_fileno = fdesc; _IO_mask_flags (fp, read_write,_IO_NO_READS+_IO_NO_WRITES+_IO_IS_APPENDING); /* For append mode, send the file offset to the end of the file. Don't update the offset cache though, since the file handle is not active. */ if ((read_write & (_IO_IS_APPENDING | _IO_NO_READS)) == (_IO_IS_APPENDING | _IO_NO_READS)) { _IO_off64_t new_pos = _IO_SYSSEEK (fp, 0, _IO_seek_end); if (new_pos == _IO_pos_BAD && errno != ESPIPE) { close_not_cancel (fdesc); return NULL; } } _IO_link_in ((struct _IO_FILE_plus *) fp); return fp; }
/* Should other OSes (e.g., Hurd) have different versions which can be written in a better way? */ static void check_one_fd (int fd, int mode) { /* Note that fcntl() with this parameter is not a cancellation point. */ if (__builtin_expect (__libc_fcntl (fd, F_GETFD), 0) == -1 && errno == EBADF) { const char *name; dev_t dev; /* For writable descriptors we use /dev/full. */ if ((mode & O_ACCMODE) == O_WRONLY) { name = _PATH_DEV "full"; dev = makedev (DEV_FULL_MAJOR, DEV_FULL_MINOR); } else { name = _PATH_DEVNULL; dev = makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR); } /* Something is wrong with this descriptor, it's probably not opened. Open /dev/null so that the SUID program we are about to start does not accidently use this descriptor. */ int nullfd = open_not_cancel (name, mode, 0); /* We are very paranoid here. With all means we try to ensure that we are actually opening the /dev/null device and nothing else. Note that the following code assumes that STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO are the three lowest file decsriptor numbers, in this order. */ struct stat64 st; if (__builtin_expect (nullfd != fd, 0) || __builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) != 0 || __builtin_expect (S_ISCHR (st.st_mode), 1) == 0 || st.st_rdev != dev) /* We cannot even give an error message here since it would run into the same problems. */ while (1) /* Try for ever and ever. */ ABORT_INSTRUCTION; } }
int daemon(int nochdir, int noclose) { int fd; if (fork_parent() == -1) return -1; if (setsid() == -1) return -1; if (!nochdir) chdir("/"); if (!noclose) { struct STAT st; if ((fd = open_not_cancel(_PATH_DEVNULL, O_RDWR, 0)) != -1 && (__builtin_expect (FSTAT (fd, &st), 0) == 0)) { if (__builtin_expect (S_ISCHR (st.st_mode), 1) != 0) { dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); if (fd > 2) close(fd); } else { /* We must set an errno value since no function call actually failed. */ close_not_cancel_no_status (fd); __set_errno (ENODEV); return -1; } } else { close_not_cancel_no_status (fd); return -1; } } return 0; }
/* Spawn a new process executing PATH with the attributes describes in *ATTRP. Before running the process perform the actions described in FILE-ACTIONS. */ int __spawni (pid_t *pid, const char *file, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], int xflags) { pid_t new_pid; char *path, *p, *name; size_t len; size_t pathlen; /* Do this once. */ short int flags = attrp == NULL ? 0 : attrp->__flags; /* Generate the new process. */ if ((flags & POSIX_SPAWN_USEVFORK) != 0 /* If no major work is done, allow using vfork. Note that we might perform the path searching. But this would be done by a call to execvp(), too, and such a call must be OK according to POSIX. */ || ((flags & (POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_RESETIDS)) == 0 && file_actions == NULL)) new_pid = __vfork (); else new_pid = __fork (); if (new_pid != 0) { if (new_pid < 0) return errno; /* The call was successful. Store the PID if necessary. */ if (pid != NULL) *pid = new_pid; return 0; } /* Set signal mask. */ if ((flags & POSIX_SPAWN_SETSIGMASK) != 0 && __sigprocmask (SIG_SETMASK, &attrp->__ss, NULL) != 0) _exit (SPAWN_ERROR); /* Set signal default action. */ if ((flags & POSIX_SPAWN_SETSIGDEF) != 0) { /* We have to iterate over all signals. This could possibly be done better but it requires system specific solutions since the sigset_t data type can be very different on different architectures. */ int sig; struct sigaction sa; memset (&sa, '\0', sizeof (sa)); sa.sa_handler = SIG_DFL; for (sig = 1; sig <= _NSIG; ++sig) if (__sigismember (&attrp->__sd, sig) != 0 && __sigaction (sig, &sa, NULL) != 0) _exit (SPAWN_ERROR); } #ifdef _POSIX_PRIORITY_SCHEDULING /* Set the scheduling algorithm and parameters. */ if ((flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER)) == POSIX_SPAWN_SETSCHEDPARAM) { if (__sched_setparam (0, &attrp->__sp) == -1) _exit (SPAWN_ERROR); } else if ((flags & POSIX_SPAWN_SETSCHEDULER) != 0) { if (__sched_setscheduler (0, attrp->__policy, &attrp->__sp) == -1) _exit (SPAWN_ERROR); } #endif /* Set the process group ID. */ if ((flags & POSIX_SPAWN_SETPGROUP) != 0 && __setpgid (0, attrp->__pgrp) != 0) _exit (SPAWN_ERROR); /* Set the effective user and group IDs. */ if ((flags & POSIX_SPAWN_RESETIDS) != 0 && (local_seteuid (__getuid ()) != 0 || local_setegid (__getgid ()) != 0)) _exit (SPAWN_ERROR); /* Execute the file actions. */ if (file_actions != NULL) { int cnt; struct rlimit64 fdlimit; bool have_fdlimit = false; for (cnt = 0; cnt < file_actions->__used; ++cnt) { struct __spawn_action *action = &file_actions->__actions[cnt]; switch (action->tag) { case spawn_do_close: if (close_not_cancel (action->action.close_action.fd) != 0) { if (! have_fdlimit) { getrlimit64 (RLIMIT_NOFILE, &fdlimit); have_fdlimit = true; } /* Only signal errors for file descriptors out of range. */ if (action->action.close_action.fd < 0 || action->action.close_action.fd >= fdlimit.rlim_cur) /* Signal the error. */ _exit (SPAWN_ERROR); } break; case spawn_do_open: { int new_fd = open_not_cancel (action->action.open_action.path, action->action.open_action.oflag | O_LARGEFILE, action->action.open_action.mode); if (new_fd == -1) /* The `open' call failed. */ _exit (SPAWN_ERROR); /* Make sure the desired file descriptor is used. */ if (new_fd != action->action.open_action.fd) { if (__dup2 (new_fd, action->action.open_action.fd) != action->action.open_action.fd) /* The `dup2' call failed. */ _exit (SPAWN_ERROR); if (close_not_cancel (new_fd) != 0) /* The `close' call failed. */ _exit (SPAWN_ERROR); } } break; case spawn_do_dup2: if (__dup2 (action->action.dup2_action.fd, action->action.dup2_action.newfd) != action->action.dup2_action.newfd) /* The `dup2' call failed. */ _exit (SPAWN_ERROR); break; } } } if ((xflags & SPAWN_XFLAGS_USE_PATH) == 0 || strchr (file, '/') != NULL) { /* The FILE parameter is actually a path. */ __execve (file, argv, envp); maybe_script_execute (file, argv, envp, xflags); /* Oh, oh. `execve' returns. This is bad. */ _exit (SPAWN_ERROR); } /* We have to search for FILE on the path. */ path = getenv ("PATH"); if (path == NULL) { /* There is no `PATH' in the environment. The default search path is the current directory followed by the path `confstr' returns for `_CS_PATH'. */ len = confstr (_CS_PATH, (char *) NULL, 0); path = (char *) __alloca (1 + len); path[0] = ':'; (void) confstr (_CS_PATH, path + 1, len); } len = strlen (file) + 1; pathlen = strlen (path); name = __alloca (pathlen + len + 1); /* Copy the file name at the top. */ name = (char *) memcpy (name + pathlen + 1, file, len); /* And add the slash. */ *--name = '/'; p = path; do { char *startp; path = p; p = __strchrnul (path, ':'); if (p == path) /* Two adjacent colons, or a colon at the beginning or the end of `PATH' means to search the current directory. */ startp = name + 1; else startp = (char *) memcpy (name - (p - path), path, p - path); /* Try to execute this name. If it works, execv will not return. */ __execve (startp, argv, envp); maybe_script_execute (startp, argv, envp, xflags); switch (errno) { case EACCES: case ENOENT: case ESTALE: case ENOTDIR: /* Those errors indicate the file is missing or not executable by us, in which case we want to just try the next path directory. */ break; default: /* Some other error means we found an executable file, but something went wrong executing it; return the error to our caller. */ _exit (SPAWN_ERROR); } } while (*p++ != '\0'); /* Return with an error. */ _exit (SPAWN_ERROR); }
/* Function used in the clone call to setup the signals mask, posix_spawn attributes, and file actions. It run on its own stack (provided by the posix_spawn call). */ static int __spawni_child (void *arguments) { struct posix_spawn_args *args = arguments; const posix_spawnattr_t *restrict attr = args->attr; const posix_spawn_file_actions_t *file_actions = args->fa; int p = args->pipe[1]; int ret; close_not_cancel (args->pipe[0]); /* The child must ensure that no signal handler are enabled because it shared memory with parent, so the signal disposition must be either SIG_DFL or SIG_IGN. It does by iterating over all signals and although it could possibly be more optimized (by tracking which signal potentially have a signal handler), it might requires system specific solutions (since the sigset_t data type can be very different on different architectures). */ struct sigaction sa; memset (&sa, '\0', sizeof (sa)); sigset_t hset; __sigprocmask (SIG_BLOCK, 0, &hset); for (int sig = 1; sig < _NSIG; ++sig) { if ((attr->__flags & POSIX_SPAWN_SETSIGDEF) && sigismember (&attr->__sd, sig)) { sa.sa_handler = SIG_DFL; } else if (sigismember (&hset, sig)) { if (__nptl_is_internal_signal (sig)) sa.sa_handler = SIG_IGN; else { __libc_sigaction (sig, 0, &sa); if (sa.sa_handler == SIG_IGN) continue; sa.sa_handler = SIG_DFL; } } else continue; __libc_sigaction (sig, &sa, 0); } #ifdef _POSIX_PRIORITY_SCHEDULING /* Set the scheduling algorithm and parameters. */ if ((attr->__flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER)) == POSIX_SPAWN_SETSCHEDPARAM) { if ((ret = __sched_setparam (0, &attr->__sp)) == -1) goto fail; } else if ((attr->__flags & POSIX_SPAWN_SETSCHEDULER) != 0) { if ((ret = __sched_setscheduler (0, attr->__policy, &attr->__sp)) == -1) goto fail; } #endif /* Set the process group ID. */ if ((attr->__flags & POSIX_SPAWN_SETPGROUP) != 0 && (ret = __setpgid (0, attr->__pgrp)) != 0) goto fail; /* Set the effective user and group IDs. */ if ((attr->__flags & POSIX_SPAWN_RESETIDS) != 0 && ((ret = local_seteuid (__getuid ())) != 0 || (ret = local_setegid (__getgid ())) != 0)) goto fail; /* Execute the file actions. */ if (file_actions != 0) { int cnt; struct rlimit64 fdlimit; bool have_fdlimit = false; for (cnt = 0; cnt < file_actions->__used; ++cnt) { struct __spawn_action *action = &file_actions->__actions[cnt]; /* Dup the pipe fd onto an unoccupied one to avoid any file operation to clobber it. */ if ((action->action.close_action.fd == p) || (action->action.open_action.fd == p) || (action->action.dup2_action.fd == p)) { if ((ret = __dup (p)) < 0) goto fail; p = ret; } switch (action->tag) { case spawn_do_close: if ((ret = close_not_cancel (action->action.close_action.fd)) != 0) { if (!have_fdlimit) { __getrlimit64 (RLIMIT_NOFILE, &fdlimit); have_fdlimit = true; } /* Signal errors only for file descriptors out of range. */ if (action->action.close_action.fd < 0 || action->action.close_action.fd >= fdlimit.rlim_cur) goto fail; } break; case spawn_do_open: { ret = open_not_cancel (action->action.open_action.path, action->action. open_action.oflag | O_LARGEFILE, action->action.open_action.mode); if (ret == -1) goto fail; int new_fd = ret; /* Make sure the desired file descriptor is used. */ if (ret != action->action.open_action.fd) { if ((ret = __dup2 (new_fd, action->action.open_action.fd)) != action->action.open_action.fd) goto fail; if ((ret = close_not_cancel (new_fd)) != 0) goto fail; } } break; case spawn_do_dup2: if ((ret = __dup2 (action->action.dup2_action.fd, action->action.dup2_action.newfd)) != action->action.dup2_action.newfd) goto fail; break; } } } /* Set the initial signal mask of the child if POSIX_SPAWN_SETSIGMASK is set, otherwise restore the previous one. */ __sigprocmask (SIG_SETMASK, (attr->__flags & POSIX_SPAWN_SETSIGMASK) ? &attr->__ss : &args->oldmask, 0); args->exec (args->file, args->argv, args->envp); /* This is compatibility function required to enable posix_spawn run script without shebang definition for older posix_spawn versions (2.15). */ maybe_script_execute (args); ret = -errno; fail: /* Since sizeof errno < PIPE_BUF, the write is atomic. */ ret = -ret; if (ret) while (write_not_cancel (p, &ret, sizeof ret) < 0) continue; exit (SPAWN_ERROR); }
int internal_function __gconv_load_cache (void) { int fd; struct stat64 st; struct gconvcache_header *header; /* We cannot use the cache if the GCONV_PATH environment variable is set. */ __gconv_path_envvar = getenv ("GCONV_PATH"); if (__gconv_path_envvar != NULL) return -1; /* See whether the cache file exists. */ fd = open_not_cancel (GCONV_MODULES_CACHE, O_RDONLY, 0); if (__builtin_expect (fd, 0) == -1) /* Not available. */ return -1; /* Get information about the file. */ if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) < 0 /* We do not have to start looking at the file if it cannot contain at least the cache header. */ || (size_t) st.st_size < sizeof (struct gconvcache_header)) { close_and_exit: close_not_cancel_no_status (fd); return -1; } /* Make the file content available. */ cache_size = st.st_size; #ifdef _POSIX_MAPPED_FILES gconv_cache = __mmap (NULL, cache_size, PROT_READ, MAP_SHARED, fd, 0); if (__builtin_expect (gconv_cache == MAP_FAILED, 0)) #endif { size_t already_read; gconv_cache = malloc (cache_size); if (gconv_cache == NULL) goto close_and_exit; already_read = 0; do { ssize_t n = __read (fd, (char *) gconv_cache + already_read, cache_size - already_read); if (__builtin_expect (n, 0) == -1) { free (gconv_cache); gconv_cache = NULL; goto close_and_exit; } already_read += n; } while (already_read < cache_size); cache_malloced = 1; } /* We don't need the file descriptor anymore. */ close_not_cancel_no_status (fd); /* Check the consistency. */ header = (struct gconvcache_header *) gconv_cache; if (__builtin_expect (header->magic, GCONVCACHE_MAGIC) != GCONVCACHE_MAGIC || __builtin_expect (header->string_offset >= cache_size, 0) || __builtin_expect (header->hash_offset >= cache_size, 0) || __builtin_expect (header->hash_size == 0, 0) || __builtin_expect ((header->hash_offset + header->hash_size * sizeof (struct hash_entry)) > cache_size, 0) || __builtin_expect (header->module_offset >= cache_size, 0) || __builtin_expect (header->otherconv_offset > cache_size, 0)) { if (cache_malloced) { free (gconv_cache); cache_malloced = 0; } #ifdef _POSIX_MAPPED_FILES else __munmap (gconv_cache, cache_size); #endif gconv_cache = NULL; return -1; } /* That worked. */ return 0; }