static int recursive_relabel(Item *i, const char *path) { int r; struct stat st; r = item_set_perms(i, path); if (r < 0) return r; if (lstat(path, &st) < 0) return -errno; if (S_ISDIR(st.st_mode)) r = recursive_relabel_children(i, path); return r; }
static int create_item(Item *i) { int r, e; mode_t u; struct stat st; assert(i); switch (i->type) { case IGNORE_PATH: case REMOVE_PATH: case RECURSIVE_REMOVE_PATH: return 0; case CREATE_FILE: case TRUNCATE_FILE: case WRITE_FILE: { int fd, flags; flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND : i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC : 0; u = umask(0); label_context_set(i->path, S_IFREG); fd = open(i->path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode); e = errno; label_context_clear(); umask(u); errno = e; if (fd < 0) { if (i->type == WRITE_FILE && errno == ENOENT) break; log_error("Failed to create file %s: %m", i->path); return -errno; } if (i->argument) { ssize_t n; size_t l; struct iovec iovec[2]; static const char new_line = '\n'; l = strlen(i->argument); zero(iovec); iovec[0].iov_base = i->argument; iovec[0].iov_len = l; iovec[1].iov_base = (void*) &new_line; iovec[1].iov_len = 1; n = writev(fd, iovec, 2); /* It's OK if we don't write the trailing * newline, hence we check for l, instead of * l+1 here. Files in /sys often refuse * writing of the trailing newline. */ if (n < 0 || (size_t) n < l) { log_error("Failed to write file %s: %s", i->path, n < 0 ? strerror(-n) : "Short write"); close_nointr_nofail(fd); return n < 0 ? n : -EIO; } } close_nointr_nofail(fd); if (stat(i->path, &st) < 0) { log_error("stat(%s) failed: %m", i->path); return -errno; } if (!S_ISREG(st.st_mode)) { log_error("%s is not a file.", i->path); return -EEXIST; } r = item_set_perms(i, i->path); if (r < 0) return r; break; } case TRUNCATE_DIRECTORY: case CREATE_DIRECTORY: u = umask(0); mkdir_parents_label(i->path, 0755); r = mkdir(i->path, i->mode); umask(u); if (r < 0 && errno != EEXIST) { log_error("Failed to create directory %s: %m", i->path); return -errno; } if (stat(i->path, &st) < 0) { log_error("stat(%s) failed: %m", i->path); return -errno; } if (!S_ISDIR(st.st_mode)) { log_error("%s is not a directory.", i->path); return -EEXIST; } r = item_set_perms(i, i->path); if (r < 0) return r; break; case CREATE_FIFO: u = umask(0); r = mkfifo(i->path, i->mode); umask(u); if (r < 0 && errno != EEXIST) { log_error("Failed to create fifo %s: %m", i->path); return -errno; } if (stat(i->path, &st) < 0) { log_error("stat(%s) failed: %m", i->path); return -errno; } if (!S_ISFIFO(st.st_mode)) { log_error("%s is not a fifo.", i->path); return -EEXIST; } r = item_set_perms(i, i->path); if (r < 0) return r; break; case CREATE_SYMLINK: { char *x; label_context_set(i->path, S_IFLNK); r = symlink(i->argument, i->path); e = errno; label_context_clear(); errno = e; if (r < 0 && errno != EEXIST) { log_error("symlink(%s, %s) failed: %m", i->argument, i->path); return -errno; } r = readlink_malloc(i->path, &x); if (r < 0) { log_error("readlink(%s) failed: %s", i->path, strerror(-r)); return -errno; } if (!streq(i->argument, x)) { free(x); log_error("%s is not the right symlinks.", i->path); return -EEXIST; } free(x); break; } case CREATE_BLOCK_DEVICE: case CREATE_CHAR_DEVICE: { mode_t file_type = (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR); u = umask(0); label_context_set(i->path, file_type); r = mknod(i->path, i->mode | file_type, i->major_minor); e = errno; label_context_clear(); umask(u); errno = e; if (r < 0 && errno != EEXIST) { log_error("Failed to create device node %s: %m", i->path); return -errno; } if (stat(i->path, &st) < 0) { log_error("stat(%s) failed: %m", i->path); return -errno; } if ((st.st_mode & S_IFMT) != file_type) { log_error("%s is not a device node.", i->path); return -EEXIST; } r = item_set_perms(i, i->path); if (r < 0) return r; break; } case RELABEL_PATH: r = glob_item(i, item_set_perms); if (r < 0) return 0; break; case RECURSIVE_RELABEL_PATH: r = glob_item(i, recursive_relabel); if (r < 0) return r; } log_debug("%s created successfully.", i->path); return 0; }
static int create_item(Item *i) { int r, e; mode_t u; struct stat st; assert(i); switch (i->type) { case IGNORE_PATH: case IGNORE_DIRECTORY_PATH: case REMOVE_PATH: case RECURSIVE_REMOVE_PATH: return 0; case CREATE_FILE: case TRUNCATE_FILE: r = write_one_file(i, i->path); if (r < 0) return r; break; case WRITE_FILE: r = glob_item(i, write_one_file); if (r < 0) return r; break; case TRUNCATE_DIRECTORY: case CREATE_DIRECTORY: u = umask(0); mkdir_parents_label(i->path, 0755); r = mkdir(i->path, i->mode); umask(u); if (r < 0 && errno != EEXIST) { log_error("Failed to create directory %s: %m", i->path); return -errno; } if (stat(i->path, &st) < 0) { log_error("stat(%s) failed: %m", i->path); return -errno; } if (!S_ISDIR(st.st_mode)) { log_error("%s is not a directory.", i->path); return -EEXIST; } r = item_set_perms(i, i->path); if (r < 0) return r; break; case CREATE_FIFO: u = umask(0); r = mkfifo(i->path, i->mode); umask(u); if (r < 0 && errno != EEXIST) { log_error("Failed to create fifo %s: %m", i->path); return -errno; } if (stat(i->path, &st) < 0) { log_error("stat(%s) failed: %m", i->path); return -errno; } if (!S_ISFIFO(st.st_mode)) { log_error("%s is not a fifo.", i->path); return -EEXIST; } r = item_set_perms(i, i->path); if (r < 0) return r; break; case CREATE_SYMLINK: { char *x; label_context_set(i->path, S_IFLNK); r = symlink(i->argument, i->path); e = errno; label_context_clear(); errno = e; if (r < 0 && errno != EEXIST) { log_error("symlink(%s, %s) failed: %m", i->argument, i->path); return -errno; } r = readlink_malloc(i->path, &x); if (r < 0) { log_error("readlink(%s) failed: %s", i->path, strerror(-r)); return -errno; } if (!streq(i->argument, x)) { free(x); log_error("%s is not the right symlinks.", i->path); return -EEXIST; } free(x); break; } case CREATE_BLOCK_DEVICE: case CREATE_CHAR_DEVICE: { mode_t file_type; if (have_effective_cap(CAP_MKNOD) == 0) { /* In a container we lack CAP_MKNOD. We shouldnt attempt to create the device node in that case to avoid noise, and we don't support virtualized devices in containers anyway. */ log_debug("We lack CAP_MKNOD, skipping creation of device node %s.", i->path); return 0; } file_type = (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR); u = umask(0); label_context_set(i->path, file_type); r = mknod(i->path, i->mode | file_type, i->major_minor); e = errno; label_context_clear(); umask(u); errno = e; if (r < 0 && errno != EEXIST) { log_error("Failed to create device node %s: %m", i->path); return -errno; } if (stat(i->path, &st) < 0) { log_error("stat(%s) failed: %m", i->path); return -errno; } if ((st.st_mode & S_IFMT) != file_type) { log_error("%s is not a device node.", i->path); return -EEXIST; } r = item_set_perms(i, i->path); if (r < 0) return r; break; } case RELABEL_PATH: r = glob_item(i, item_set_perms); if (r < 0) return 0; break; case RECURSIVE_RELABEL_PATH: r = glob_item(i, recursive_relabel); if (r < 0) return r; } log_debug("%s created successfully.", i->path); return 0; }
static int recursive_relabel_children(Item *i, const char *path) { DIR *d; int ret = 0; /* This returns the first error we run into, but nevertheless * tries to go on */ d = opendir(path); if (!d) return errno == ENOENT ? 0 : -errno; for (;;) { struct dirent buf, *de; bool is_dir; int r; char *entry_path; r = readdir_r(d, &buf, &de); if (r != 0) { if (ret == 0) ret = -r; break; } if (!de) break; if (streq(de->d_name, ".") || streq(de->d_name, "..")) continue; if (asprintf(&entry_path, "%s/%s", path, de->d_name) < 0) { if (ret == 0) ret = -ENOMEM; continue; } if (de->d_type == DT_UNKNOWN) { struct stat st; if (lstat(entry_path, &st) < 0) { if (ret == 0 && errno != ENOENT) ret = -errno; free(entry_path); continue; } is_dir = S_ISDIR(st.st_mode); } else is_dir = de->d_type == DT_DIR; r = item_set_perms(i, entry_path); if (r < 0) { if (ret == 0 && r != -ENOENT) ret = r; free(entry_path); continue; } if (is_dir) { r = recursive_relabel_children(i, entry_path); if (r < 0 && ret == 0) ret = r; } free(entry_path); } closedir(d); return ret; }
static int write_one_file(Item *i, const char *path) { int r, e, fd, flags; struct stat st; mode_t u; flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND : i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC : 0; u = umask(0); label_context_set(path, S_IFREG); fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode); e = errno; label_context_clear(); umask(u); errno = e; if (fd < 0) { if (i->type == WRITE_FILE && errno == ENOENT) return 0; log_error("Failed to create file %s: %m", path); return -errno; } if (i->argument) { ssize_t n; size_t l; _cleanup_free_ char *unescaped; unescaped = cunescape(i->argument); if (unescaped == NULL) { close_nointr_nofail(fd); return log_oom(); } l = strlen(unescaped); n = write(fd, unescaped, l); if (n < 0 || (size_t) n < l) { log_error("Failed to write file %s: %s", path, n < 0 ? strerror(-n) : "Short write"); close_nointr_nofail(fd); return n < 0 ? n : -EIO; } } close_nointr_nofail(fd); if (stat(path, &st) < 0) { log_error("stat(%s) failed: %m", path); return -errno; } if (!S_ISREG(st.st_mode)) { log_error("%s is not a file.", path); return -EEXIST; } r = item_set_perms(i, path); if (r < 0) return r; return 0; }