1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 int copy_bytes(int fdf, int fdt) {
33 n = read(fdf, buf, sizeof(buf));
40 k = loop_write(fdt, buf, n, false);
44 return errno ? -errno : -EIO;
50 static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) {
51 _cleanup_free_ char *target = NULL;
58 r = readlinkat_malloc(df, from, &target);
62 if (symlinkat(target, dt, to) < 0) {
69 if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
75 static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) {
76 _cleanup_close_ int fdf = -1, fdt = -1;
83 fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
87 fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777);
95 r = copy_bytes(fdf, fdt);
101 if (fchown(fdt, st->st_uid, st->st_gid) < 0)
104 if (fchmod(fdt, st->st_mode & 07777) < 0)
118 static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) {
125 r = mkfifoat(dt, to, st->st_mode & 07777);
133 if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
136 if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
142 static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) {
149 r = mknodat(dt, to, st->st_mode, st->st_rdev);
157 if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
160 if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
166 static int fd_copy_directory(int df, const char *from, const struct stat *st, int dt, const char *to, dev_t original_device) {
167 _cleanup_close_ int fdf = -1, fdt = -1;
168 _cleanup_closedir_ DIR *d = NULL;
177 fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
186 r = mkdirat(dt, to, st->st_mode & 07777);
189 else if (errno == EEXIST)
194 fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
199 if (fchown(fdt, st->st_uid, st->st_gid) < 0)
202 if (fchmod(fdt, st->st_mode & 07777) < 0)
207 FOREACH_DIRENT(de, d, return -errno) {
211 if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
216 if (buf.st_dev != original_device)
219 if (S_ISREG(buf.st_mode))
220 q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name);
221 else if (S_ISDIR(buf.st_mode))
222 q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device);
223 else if (S_ISLNK(buf.st_mode))
224 q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
225 else if (S_ISFIFO(buf.st_mode))
226 q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
227 else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
228 q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
239 int copy_tree(const char *from, const char *to) {
245 if (lstat(from, &st) < 0)
248 if (S_ISREG(st.st_mode))
249 return fd_copy_regular(AT_FDCWD, from, &st, AT_FDCWD, to);
250 else if (S_ISDIR(st.st_mode))
251 return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev);
252 else if (S_ISLNK(st.st_mode))
253 return fd_copy_symlink(AT_FDCWD, from, &st, AT_FDCWD, to);
254 else if (S_ISFIFO(st.st_mode))
255 return fd_copy_fifo(AT_FDCWD, from, &st, AT_FDCWD, to);
256 else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
257 return fd_copy_node(AT_FDCWD, from, &st, AT_FDCWD, to);
262 int copy_file(const char *from, const char *to, int flags, mode_t mode) {
263 _cleanup_close_ int fdf = -1, fdt = -1;
269 fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
273 fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
277 r = copy_bytes(fdf, fdt);