mkdir-parents.c revision 766814de3fbfeb8ca7c4d6b592f6ca09f14d5690
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2003-2012 Dovecot authors, see the included COPYING file */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "lib.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "str.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "eacces-error.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "mkdir-parents.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include "ipwd.h"
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include <sys/stat.h>
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen#include <unistd.h>
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
551fef69c0633ae3c7738038e047a7c0762d9599Timo Sirainenstatic int
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenmkdir_chown_full(const char *path, mode_t mode, uid_t uid,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen gid_t gid, const char *gid_origin)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen string_t *str;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen mode_t old_mask;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen int ret, orig_errno;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen old_mask = umask(0);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ret = mkdir(path, mode);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen umask(old_mask);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (ret < 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (errno == EISDIR || errno == ENOSYS) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* EISDIR check is for BSD/OS which returns it if path
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen contains '/' at the end and it exists.
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ENOSYS check is for NFS mount points. */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen errno = EEXIST;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (chown(path, uid, gid) < 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen orig_errno = errno;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (rmdir(path) < 0)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen i_error("rmdir(%s) failed: %m", path);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen errno = orig_errno;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (errno == EPERM && uid == (uid_t)-1) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen i_error("%s", eperm_error_get_chgrp("chown", path, gid,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen gid_origin));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen str = t_str_new(256);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen str_printfa(str, "chown(%s, %ld", path,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen uid == (uid_t)-1 ? -1L : (long)uid);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (uid != (uid_t)-1) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct passwd pw;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (i_getpwuid(uid, &pw) > 0)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen str_printfa(str, "(%s)", pw.pw_name);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen str_printfa(str, ", %ld",
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen gid == (gid_t)-1 ? -1L : (long)gid);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (gid != (gid_t)-1) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct group gr;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (i_getgrgid(uid, &gr) > 0)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen str_printfa(str, "(%s)", gr.gr_name);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen errno = orig_errno;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen i_error("%s) failed: %m", str_c(str));
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenint mkdir_chown(const char *path, mode_t mode, uid_t uid, gid_t gid)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return mkdir_chown_full(path, mode, uid, gid, NULL);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenint mkdir_chgrp(const char *path, mode_t mode,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen gid_t gid, const char *gid_origin)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return mkdir_chown_full(path, mode, (uid_t)-1, gid, gid_origin);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenstatic int
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenmkdir_parents_chown_full(const char *path, mode_t mode, uid_t uid, gid_t gid,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const char *gid_origin)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const char *p;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen int ret;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (mkdir_chown_full(path, mode, uid, gid, gid_origin) < 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (errno != ENOENT)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* doesn't exist, try recursively creating our parent dir */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen p = strrchr(path, '/');
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (p == NULL || p == path)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1; /* shouldn't happen */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen T_BEGIN {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen ret = mkdir_parents_chown_full(t_strdup_until(path, p),
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen mode, uid,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen gid, gid_origin);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen } T_END;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (ret < 0)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen /* should work now */
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (mkdir_chown_full(path, mode, uid, gid, gid_origin) < 0)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenint mkdir_parents_chown(const char *path, mode_t mode, uid_t uid, gid_t gid)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return mkdir_parents_chown_full(path, mode, uid, gid, NULL);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenint mkdir_parents_chgrp(const char *path, mode_t mode,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen gid_t gid, const char *gid_origin)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return mkdir_parents_chown_full(path, mode, (uid_t)-1, gid, gid_origin);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenint mkdir_parents(const char *path, mode_t mode)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return mkdir_parents_chown(path, mode, (uid_t)-1, (gid_t)-1);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainenint stat_first_parent(const char *path, const char **root_dir_r,
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen struct stat *st_r)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen{
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen const char *p;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen while (stat(path, st_r) < 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (errno != ENOENT || strcmp(path, "/") == 0) {
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *root_dir_r = path;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return -1;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen p = strrchr(path, '/');
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen if (p == NULL)
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen path = "/";
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen else
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen path = t_strdup_until(path, p);
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen }
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen *root_dir_r = path;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen return 0;
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen}
5ef28f68edef46f69961b19b7c1dcd8ec5a955e8Timo Sirainen