mkdir-parents.c revision a6bb10d402b5f28af4c31a4daa07e47146a3f897
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch/* Copyright (c) 2003-2015 Dovecot authors, see the included COPYING file */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch#include "lib.h"
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch#include "str.h"
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch#include "eacces-error.h"
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch#include "mkdir-parents.h"
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch#include "ipwd.h"
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch#include <sys/stat.h>
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch#include <fcntl.h>
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch#include <unistd.h>
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschstatic int ATTR_NULL(5)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschmkdir_chown_full(const char *path, mode_t mode, uid_t uid,
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch gid_t gid, const char *gid_origin)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch{
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch string_t *str;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch mode_t old_mask;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch unsigned int i;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch int ret, fd = -1, orig_errno;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch for (i = 0;; i++) {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch old_mask = umask(0);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch ret = mkdir(path, mode);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch umask(old_mask);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (ret < 0)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch break;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch fd = open(path, O_RDONLY);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (fd != -1)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch break;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (errno != ENOENT || i == 3) {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch i_error("open(%s) failed: %m", path);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch return -1;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch }
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch /* it was just rmdir()ed by someone else? retry */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch }
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (ret < 0) {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (errno == EISDIR || errno == ENOSYS) {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch /* EISDIR check is for BSD/OS which returns it if path
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch contains '/' at the end and it exists.
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch ENOSYS check is for NFS mount points. */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch errno = EEXIST;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch }
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch i_assert(fd == -1);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch return -1;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch }
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (fchown(fd, uid, gid) < 0) {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch i_close_fd(&fd);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch orig_errno = errno;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (rmdir(path) < 0 && errno != ENOENT)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch i_error("rmdir(%s) failed: %m", path);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch errno = orig_errno;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (errno == EPERM && uid == (uid_t)-1) {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch i_error("%s", eperm_error_get_chgrp("fchown", path, gid,
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch gid_origin));
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch return -1;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch }
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch str = t_str_new(256);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch str_printfa(str, "fchown(%s, %ld", path,
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch uid == (uid_t)-1 ? -1L : (long)uid);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (uid != (uid_t)-1) {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch struct passwd pw;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (i_getpwuid(uid, &pw) > 0)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch str_printfa(str, "(%s)", pw.pw_name);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch }
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch str_printfa(str, ", %ld",
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch gid == (gid_t)-1 ? -1L : (long)gid);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (gid != (gid_t)-1) {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch struct group gr;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (i_getgrgid(uid, &gr) > 0)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch str_printfa(str, "(%s)", gr.gr_name);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch }
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch errno = orig_errno;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch i_error("%s) failed: %m", str_c(str));
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch return -1;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch }
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (gid != (gid_t)-1 && (mode & S_ISGID) == 0) {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch /* make sure the directory doesn't have setgid bit enabled
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch (in case its parent had) */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (fchmod(fd, mode) < 0) {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch orig_errno = errno;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (rmdir(path) < 0 && errno != ENOENT)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch i_error("rmdir(%s) failed: %m", path);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch errno = orig_errno;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch i_error("fchmod(%s) failed: %m", path);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch i_close_fd(&fd);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch return -1;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch }
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch }
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch i_close_fd(&fd);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch return 0;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch}
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschint mkdir_chown(const char *path, mode_t mode, uid_t uid, gid_t gid)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch{
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch return mkdir_chown_full(path, mode, uid, gid, NULL);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch}
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschint mkdir_chgrp(const char *path, mode_t mode,
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch gid_t gid, const char *gid_origin)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch{
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch return mkdir_chown_full(path, mode, (uid_t)-1, gid, gid_origin);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch}
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschstatic int ATTR_NULL(5)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschmkdir_parents_chown_full(const char *path, mode_t mode, uid_t uid, gid_t gid,
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch const char *gid_origin)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch{
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch const char *p;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch int ret;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (mkdir_chown_full(path, mode, uid, gid, gid_origin) < 0) {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (errno != ENOENT)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch return -1;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch /* doesn't exist, try recursively creating our parent dir */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch p = strrchr(path, '/');
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (p == NULL || p == path)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch return -1; /* shouldn't happen */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch T_BEGIN {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch ret = mkdir_parents_chown_full(t_strdup_until(path, p),
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch mode, uid,
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch gid, gid_origin);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch } T_END;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (ret < 0 && errno != EEXIST)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch return -1;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch /* should work now */
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (mkdir_chown_full(path, mode, uid, gid, gid_origin) < 0)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch return -1;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch }
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch return 0;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch}
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschint mkdir_parents_chown(const char *path, mode_t mode, uid_t uid, gid_t gid)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch{
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch return mkdir_parents_chown_full(path, mode, uid, gid, NULL);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch}
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschint mkdir_parents_chgrp(const char *path, mode_t mode,
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch gid_t gid, const char *gid_origin)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch{
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch return mkdir_parents_chown_full(path, mode, (uid_t)-1, gid, gid_origin);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch}
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschint mkdir_parents(const char *path, mode_t mode)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch{
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch return mkdir_parents_chown(path, mode, (uid_t)-1, (gid_t)-1);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch}
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Boschint stat_first_parent(const char *path, const char **root_dir_r,
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch struct stat *st_r)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch{
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch const char *p;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch while (stat(path, st_r) < 0) {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (errno != ENOENT || strcmp(path, "/") == 0) {
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch *root_dir_r = path;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch return -1;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch }
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch p = strrchr(path, '/');
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch if (p == NULL)
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch path = "/";
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch else
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch path = t_strdup_until(path, p);
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch }
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch *root_dir_r = path;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch return 0;
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch}
f9511e684858bf5f6ac77ab12254b85b737beae8Stephan Bosch