#include <glob.h>
#include <grp.h>
#include <sys/mman.h>
+#include <sys/vfs.h>
+#include <linux/magic.h>
#include "macro.h"
#include "util.h"
if (h < 0)
return h;
- r = join("[", t, "]", NULL);
+ r = strjoin("[", t, "]", NULL);
free(t);
if (!r)
*/
for (;;) {
- if ((fd = open(name, mode)) >= 0)
+ fd = open(name, mode);
+ if (fd >= 0)
break;
if (errno != EIO)
return -errno;
+ /* Max 1s in total */
if (c >= 20)
return -errno;
if (fd < 0)
return -errno;
- if ((r = isatty(fd)) < 0) {
+ r = isatty(fd);
+ if (r < 0) {
close_nointr_nofail(fd);
return -errno;
}
}
}
-int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm) {
+int acquire_terminal(
+ const char *name,
+ bool fail,
+ bool force,
+ bool ignore_tiocstty_eperm,
+ usec_t timeout) {
+
int fd = -1, notify = -1, r, wd = -1;
+ usec_t ts = 0;
assert(name);
* on the same tty as an untrusted user this should not be a
* problem. (Which he probably should not do anyway.) */
+ if (timeout != (usec_t) -1)
+ ts = now(CLOCK_MONOTONIC);
+
if (!fail && !force) {
- if ((notify = inotify_init1(IN_CLOEXEC)) < 0) {
+ notify = inotify_init1(IN_CLOEXEC | (timeout != (usec_t) -1 ? IN_NONBLOCK : 0));
+ if (notify < 0) {
r = -errno;
goto fail;
}
- if ((wd = inotify_add_watch(notify, name, IN_CLOSE)) < 0) {
+ wd = inotify_add_watch(notify, name, IN_CLOSE);
+ if (wd < 0) {
r = -errno;
goto fail;
}
}
for (;;) {
- if (notify >= 0)
- if ((r = flush_fd(notify)) < 0)
+ if (notify >= 0) {
+ r = flush_fd(notify);
+ if (r < 0)
goto fail;
+ }
/* We pass here O_NOCTTY only so that we can check the return
* value TIOCSCTTY and have a reliable way to figure out if we
* successfully became the controlling process of the tty */
- if ((fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0)
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
return fd;
/* First, try to get the tty */
ssize_t l;
struct inotify_event *e;
- if ((l = read(notify, inotify_buffer, sizeof(inotify_buffer))) < 0) {
+ if (timeout != (usec_t) -1) {
+ usec_t n;
+
+ n = now(CLOCK_MONOTONIC);
+ if (ts + timeout < n) {
+ r = -ETIMEDOUT;
+ goto fail;
+ }
+
+ r = fd_wait_for_event(fd, POLLIN, ts + timeout - n);
+ if (r < 0)
+ goto fail;
- if (errno == EINTR)
+ if (r == 0) {
+ r = -ETIMEDOUT;
+ goto fail;
+ }
+ }
+
+ l = read(notify, inotify_buffer, sizeof(inotify_buffer));
+ if (l < 0) {
+
+ if (errno == EINTR || errno == EAGAIN)
continue;
r = -errno;
int make_null_stdio(void) {
int null_fd;
- if ((null_fd = open("/dev/null", O_RDWR|O_NOCTTY)) < 0)
+ null_fd = open("/dev/null", O_RDWR|O_NOCTTY);
+ if (null_fd < 0)
return -errno;
return make_stdio(null_fd);
return 0;
}
-int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
+int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
DIR *d;
int ret = 0;
return ret;
}
-int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
- int fd;
- int r;
+int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
+ struct statfs s;
+
+ assert(fd >= 0);
+
+ if (fstatfs(fd, &s) < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ /* We refuse to clean disk file systems with this call. This
+ * is extra paranoia just to be sure we never ever remove
+ * non-state data */
+
+ if (s.f_type != TMPFS_MAGIC &&
+ s.f_type != RAMFS_MAGIC) {
+ log_error("Attempted to remove disk file system, and we can't allow that.");
+ close_nointr_nofail(fd);
+ return -EPERM;
+ }
+
+ return rm_rf_children_dangerous(fd, only_dirs, honour_sticky, root_dev);
+}
+
+static int rm_rf_internal(const char *path, bool only_dirs, bool delete_root, bool honour_sticky, bool dangerous) {
+ int fd, r;
+ struct statfs s;
assert(path);
+ /* We refuse to clean the root file system with this
+ * call. This is extra paranoia to never cause a really
+ * seriously broken system. */
+ if (path_equal(path, "/")) {
+ log_error("Attempted to remove entire root file system, and we can't allow that.");
+ return -EPERM;
+ }
+
fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
if (fd < 0) {
if (errno != ENOTDIR)
return -errno;
+ if (!dangerous) {
+ if (statfs(path, &s) < 0)
+ return -errno;
+
+ if (s.f_type != TMPFS_MAGIC &&
+ s.f_type != RAMFS_MAGIC) {
+ log_error("Attempted to remove disk file system, and we can't allow that.");
+ return -EPERM;
+ }
+ }
+
if (delete_root && !only_dirs)
if (unlink(path) < 0 && errno != ENOENT)
return -errno;
return 0;
}
- r = rm_rf_children(fd, only_dirs, honour_sticky, NULL);
+ if (!dangerous) {
+ if (fstatfs(fd, &s) < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ if (s.f_type != TMPFS_MAGIC &&
+ s.f_type != RAMFS_MAGIC) {
+ log_error("Attempted to remove disk file system, and we can't allow that.");
+ close_nointr_nofail(fd);
+ return -EPERM;
+ }
+ }
+ r = rm_rf_children_dangerous(fd, only_dirs, honour_sticky, NULL);
if (delete_root) {
if (honour_sticky && file_is_priv_sticky(path) > 0)
return r;
}
+int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
+ return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, false);
+}
+
+int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
+ return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, true);
+}
+
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
assert(path);
return r;
}
-char *join(const char *x, ...) {
+char *strjoin(const char *x, ...) {
va_list ap;
size_t l;
char *r, *p;
bool in_initrd(void) {
static int saved = -1;
+ struct statfs s;
- if (saved < 0)
- saved = access("/etc/initrd-release", F_OK) >= 0;
+ if (saved >= 0)
+ return saved;
+
+ /* We make two checks here:
+ *
+ * 1. the flag file /etc/initrd-release must exist
+ * 2. the root file system must be a memory file system
+ *
+ * The second check is extra paranoia, since misdetecting an
+ * initrd can have bad bad consequences due the initrd
+ * emptying when transititioning to the main systemd.
+ */
+
+ saved = access("/etc/initrd-release", F_OK) >= 0 &&
+ statfs("/", &s) >= 0 &&
+ (s.f_type == TMPFS_MAGIC || s.f_type == RAMFS_MAGIC);
return saved;
}
ioctl(fd, KIOCSOUND, 0);
close_nointr_nofail(fd);
}
+
+int make_console_stdio(void) {
+ int fd, r;
+
+ /* Make /dev/console the controlling terminal and stdin/stdout/stderr */
+
+ fd = acquire_terminal("/dev/console", false, true, true, (usec_t) -1);
+ if (fd < 0) {
+ log_error("Failed to acquire terminal: %s", strerror(-fd));
+ return fd;
+ }
+
+ r = make_stdio(fd);
+ if (r < 0) {
+ log_error("Failed to duplicate terminal fd: %s", strerror(-r));
+ return r;
+ }
+
+ return 0;
+}