1/*** 2 This file is part of eudev, forked from systemd. 3 4 Copyright 2010 Lennart Poettering 5 6 systemd is free software; you can redistribute it and/or modify it 7 under the terms of the GNU Lesser General Public License as published by 8 the Free Software Foundation; either version 2.1 of the License, or 9 (at your option) any later version. 10 11 systemd is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public License 17 along with systemd; If not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#include <assert.h> 21#include <string.h> 22#include <unistd.h> 23#include <errno.h> 24#include <stdlib.h> 25#include <stdio.h> 26 27#include "label.h" 28#include "util.h" 29#include "path-util.h" 30#include "mkdir.h" 31 32int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkdir_func_t _mkdir) { 33 struct stat st; 34 35 if (_mkdir(path, mode) >= 0) 36 if (chmod_and_chown(path, mode, uid, gid) < 0) 37 return -errno; 38 39 if (lstat(path, &st) < 0) 40 return -errno; 41 42 if ((st.st_mode & 0007) > (mode & 0007) || 43 (st.st_mode & 0070) > (mode & 0070) || 44 (st.st_mode & 0700) > (mode & 0700) || 45 (uid != UID_INVALID && st.st_uid != uid) || 46 (gid != GID_INVALID && st.st_gid != gid) || 47 !S_ISDIR(st.st_mode)) 48 return -EEXIST; 49 50 return 0; 51} 52 53int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) { 54 const char *p, *e; 55 int r; 56 57 assert(path); 58 59 if (prefix && !path_startswith(path, prefix)) 60 return -ENOTDIR; 61 62 /* return immediately if directory exists */ 63 e = strrchr(path, '/'); 64 if (!e) 65 return -EINVAL; 66 67 if (e == path) 68 return 0; 69 70 char buf[PATH_MAX + 1]; 71 p = buf; 72 assert(e-path < sizeof(buf)); 73 memcpy(buf, path, e-path); 74 buf[e-path] = 0; 75 76 r = is_dir(p, true); 77 if (r > 0) 78 return 0; 79 if (r == 0) 80 return -ENOTDIR; 81 82 /* create every parent directory in the path, except the last component */ 83 p = path + strspn(path, "/"); 84 for (;;) { 85 char t[strlen(path) + 1]; 86 87 e = p + strcspn(p, "/"); 88 p = e + strspn(e, "/"); 89 90 /* Is this the last component? If so, then we're 91 * done */ 92 if (*p == 0) 93 return 0; 94 95 memcpy(t, path, e - path); 96 t[e-path] = 0; 97 98 if (prefix && path_startswith(prefix, t)) 99 continue; 100 101 r = _mkdir(t, mode); 102 if (r < 0 && errno != EEXIST) 103 return -errno; 104 } 105} 106 107int mkdir_parents(const char *path, mode_t mode) { 108 return mkdir_parents_internal(NULL, path, mode, mkdir); 109} 110 111int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) { 112 int r; 113 114 /* Like mkdir -p */ 115 116 r = mkdir_parents_internal(prefix, path, mode, _mkdir); 117 if (r < 0) 118 return r; 119 120 r = _mkdir(path, mode); 121 if (r < 0 && (errno != EEXIST || is_dir(path, true) <= 0)) 122 return -errno; 123 124 return 0; 125} 126 127int udev_mkdir_p(const char *path, mode_t mode) { 128 return mkdir_p_internal(NULL, path, mode, mkdir); 129} 130