mkdir-parents.c revision b710c5119d3b66572eb87a15ac6d8dcd192f7cc5
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch/* Copyright (c) 2003-2010 Dovecot authors, see the included COPYING file */
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include "lib.h"
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include "str.h"
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include "eacces-error.h"
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include "mkdir-parents.h"
833bed942977673526c72e79bccc09314fc57104Phil Carmody
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include <sys/stat.h>
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include <unistd.h>
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include <pwd.h>
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch#include <grp.h>
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Boschstatic int
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Boschmkdir_chown_full(const char *path, mode_t mode, uid_t uid,
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch gid_t gid, const char *gid_origin)
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch{
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch string_t *str;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch mode_t old_mask;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch int ret, orig_errno;
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch old_mask = umask(0);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch ret = mkdir(path, mode);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch umask(old_mask);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (ret < 0) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (errno == EISDIR || errno == ENOSYS) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch /* EISDIR check is for BSD/OS which returns it if path
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch contains '/' at the end and it exists.
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch ENOSYS check is for NFS mount points. */
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch errno = EEXIST;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return -1;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (chown(path, uid, gid) < 0) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch orig_errno = errno;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (rmdir(path) < 0)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch i_error("rmdir(%s) failed: %m", path);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch errno = orig_errno;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (errno == EPERM && uid == (uid_t)-1) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch i_error("%s", eperm_error_get_chgrp("chown", path, gid,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch gid_origin));
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return -1;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch str = t_str_new(256);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch str_printfa(str, "chown(%s, %ld", path,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch uid == (uid_t)-1 ? -1L : (long)uid);
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch if (uid != (uid_t)-1) {
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch struct passwd *pw = getpwuid(uid);
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch
5560e4cd4f5eded857471042fb5485dfa16b7c46Stephan Bosch if (pw != NULL)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch str_printfa(str, "(%s)", pw->pw_name);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch str_printfa(str, ", %ld",
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch gid == (gid_t)-1 ? -1L : (long)gid);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (gid != (gid_t)-1) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch struct group *gr = getgrgid(uid);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch if (gr != NULL)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch str_printfa(str, "(%s)", gr->gr_name);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch errno = orig_errno;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch i_error("%s) failed: %m", str_c(str));
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return -1;
7c7117e542b6a44c1db7fc91c0180bdace6dbce7Stephan Bosch }
7c7117e542b6a44c1db7fc91c0180bdace6dbce7Stephan Bosch return 0;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschint mkdir_chown(const char *path, mode_t mode, uid_t uid, gid_t gid)
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen{
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen return mkdir_chown_full(path, mode, uid, gid, NULL);
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen}
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainenint mkdir_chgrp(const char *path, mode_t mode,
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen gid_t gid, const char *gid_origin)
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen{
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen return mkdir_chown_full(path, mode, (uid_t)-1, gid, gid_origin);
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen}
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschstatic int
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschmkdir_parents_chown_full(const char *path, mode_t mode, uid_t uid, gid_t gid,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch const char *gid_origin)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch const char *p;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch int ret;
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch
dc05b1fb4b7a2b4d91248078311458cb4cbad9a1Stephan Bosch if (mkdir_chown_full(path, mode, uid, gid, gid_origin) < 0) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (errno != ENOENT)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return -1;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch /* doesn't exist, try recursively creating our parent dir */
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch p = strrchr(path, '/');
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (p == NULL || p == path)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return -1; /* shouldn't happen */
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
201c3b9375760bafbc180629b4c6ad71ed554aecStephan Bosch T_BEGIN {
201c3b9375760bafbc180629b4c6ad71ed554aecStephan Bosch ret = mkdir_parents_chown_full(t_strdup_until(path, p),
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch mode, uid,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch gid, gid_origin);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch } T_END;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (ret < 0)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return -1;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen /* should work now */
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen if (mkdir_chown_full(path, mode, uid, gid, gid_origin) < 0)
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen return -1;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return 0;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschint mkdir_parents_chown(const char *path, mode_t mode, uid_t uid, gid_t gid)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return mkdir_parents_chown_full(path, mode, uid, gid, NULL);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschint mkdir_parents_chgrp(const char *path, mode_t mode,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch gid_t gid, const char *gid_origin)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return mkdir_parents_chown_full(path, mode, (uid_t)-1, gid, gid_origin);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschint mkdir_parents(const char *path, mode_t mode)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return mkdir_parents_chown(path, mode, (uid_t)-1, (gid_t)-1);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch