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 node_symlink(struct udev_device *dev, const char *node, const char *slink) { struct stat stats; char target[UTIL_PATH_SIZE]; char *s; size_t l; char slink_tmp[UTIL_PATH_SIZE + 32]; int i = 0; int tail = 0; int err = 0; /* use relative link */ target[0] = '\0'; while (node[i] && (node[i] == slink[i])) { if (node[i] == '/') tail = i+1; i++; } s = target; l = sizeof(target); while (slink[i] != '\0') { if (slink[i] == '/') l = strpcpy(&s, l, "../"); i++; } l = strscpy(s, l, &node[tail]); if (l == 0) { err = -EINVAL; goto exit; } /* preserve link with correct target, do not replace node of other device */ if (lstat(slink, &stats) == 0) { if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { log_error("conflicting device node '%s' found, link to '%s' will not be created", slink, node); goto exit; } else if (S_ISLNK(stats.st_mode)) { char buf[UTIL_PATH_SIZE]; int len; len = readlink(slink, buf, sizeof(buf)); if (len > 0 && len < (int)sizeof(buf)) { buf[len] = '\0'; if (streq(target, buf)) { log_debug("preserve already existing symlink '%s' to '%s'", slink, target); label_fix(slink, true, false); utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW); goto exit; } } } } else { log_debug("creating symlink '%s' to '%s'", slink, target); do { err = mkdir_parents_label(slink, 0755); if (err != 0 && err != -ENOENT) break; label_context_set(slink, S_IFLNK); err = symlink(target, slink); if (err != 0) err = -errno; label_context_clear(); } while (err == -ENOENT); if (err == 0) goto exit; } log_debug("atomically replace '%s'", slink); strscpyl(slink_tmp, sizeof(slink_tmp), slink, ".tmp-", udev_device_get_id_filename(dev), NULL); unlink(slink_tmp); do { err = mkdir_parents_label(slink_tmp, 0755); if (err != 0 && err != -ENOENT) break; label_context_set(slink_tmp, S_IFLNK); err = symlink(target, slink_tmp); if (err != 0) err = -errno; label_context_clear(); } while (err == -ENOENT); if (err != 0) { log_error("symlink '%s' '%s' failed: %m", target, slink_tmp); goto exit; } err = rename(slink_tmp, slink); if (err != 0) { log_error("rename '%s' '%s' failed: %m", slink_tmp, slink); unlink(slink_tmp); } exit: return err; }
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; }