1/* See LICENSE file for copyright and license details. */
2#include <dirent.h>
3#include <errno.h>
4#include <fcntl.h>
5#include <limits.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <sys/stat.h>
10#include <sys/types.h>
11#include <unistd.h>
12#include <utime.h>
13
14#include "../fs.h"
15#include "../util.h"
16
17int cp_aflag = 0;
18int cp_fflag = 0;
19int cp_iflag = 0;
20int cp_pflag = 0;
21int cp_rflag = 0;
22int cp_vflag = 0;
23int cp_status = 0;
24int cp_follow;
25
26int
27cp(const char *s1, const char *s2, int depth)
28{
29 DIR *dp;
30 int f1, f2, flags = 0;
31 struct dirent *d;
32 struct stat st;
33 struct timespec times[2];
34 ssize_t r;
35 char target[PATH_MAX], ns1[PATH_MAX], ns2[PATH_MAX];
36
37 if (cp_follow == 'P' || (cp_follow == 'H' && depth))
38 flags |= AT_SYMLINK_NOFOLLOW;
39
40 if (fstatat(AT_FDCWD, s1, &st, flags) < 0) {
41 weprintf("stat %s:", s1);
42 cp_status = 1;
43 return 0;
44 }
45
46 if (cp_iflag && access(s2, F_OK) == 0) {
47 if (!confirm("overwrite '%s'? ", s2))
48 return 0;
49 }
50
51 if (cp_vflag)
52 printf("%s -> %s\n", s1, s2);
53
54 if (S_ISLNK(st.st_mode)) {
55 if ((r = readlink(s1, target, sizeof(target) - 1)) >= 0) {
56 target[r] = '\0';
57 if (cp_fflag && unlink(s2) < 0 && errno != ENOENT) {
58 weprintf("unlink %s:", s2);
59 cp_status = 1;
60 return 0;
61 } else if (symlink(target, s2) < 0) {
62 weprintf("symlink %s -> %s:", s2, target);
63 cp_status = 1;
64 return 0;
65 }
66 }
67 } else if (S_ISDIR(st.st_mode)) {
68 if (!cp_rflag) {
69 weprintf("%s is a directory\n", s1);
70 cp_status = 1;
71 return 0;
72 }
73 if (!(dp = opendir(s1))) {
74 weprintf("opendir %s:", s1);
75 cp_status = 1;
76 return 0;
77 }
78 if (mkdir(s2, st.st_mode) < 0 && errno != EEXIST) {
79 weprintf("mkdir %s:", s2);
80 cp_status = 1;
81 closedir(dp);
82 return 0;
83 }
84
85 while ((d = readdir(dp))) {
86 if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
87 continue;
88
89 estrlcpy(ns1, s1, sizeof(ns1));
90 if (s1[strlen(s1) - 1] != '/')
91 estrlcat(ns1, "/", sizeof(ns1));
92 estrlcat(ns1, d->d_name, sizeof(ns1));
93
94 estrlcpy(ns2, s2, sizeof(ns2));
95 if (s2[strlen(s2) - 1] != '/')
96 estrlcat(ns2, "/", sizeof(ns2));
97 estrlcat(ns2, d->d_name, sizeof(ns2));
98
99 fnck(ns1, ns2, cp, depth + 1);
100 }
101
102 closedir(dp);
103 } else if (
104 cp_aflag
105 && (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode)
106 || S_ISFIFO(st.st_mode))
107 ) {
108 if (cp_fflag && unlink(s2) < 0 && errno != ENOENT) {
109 weprintf("unlink %s:", s2);
110 cp_status = 1;
111 return 0;
112 } else if (mknod(s2, st.st_mode, st.st_rdev) < 0) {
113 weprintf("mknod %s:", s2);
114 cp_status = 1;
115 return 0;
116 }
117 } else {
118 if ((f1 = open(s1, O_RDONLY)) < 0) {
119 weprintf("open %s:", s1);
120 cp_status = 1;
121 return 0;
122 }
123 if ((f2 = creat(s2, st.st_mode)) < 0 && cp_fflag) {
124 if (unlink(s2) < 0 && errno != ENOENT) {
125 weprintf("unlink %s:", s2);
126 cp_status = 1;
127 close(f1);
128 return 0;
129 }
130 f2 = creat(s2, st.st_mode);
131 }
132 if (f2 < 0) {
133 weprintf("creat %s:", s2);
134 cp_status = 1;
135 close(f1);
136 return 0;
137 }
138 if (concat(f1, s1, f2, s2) < 0) {
139 cp_status = 1;
140 close(f1);
141 close(f2);
142 return 0;
143 }
144
145 close(f1);
146 close(f2);
147 }
148
149 if (cp_aflag || cp_pflag) {
150 /* atime and mtime */
151 times[0] = st.st_atim;
152 times[1] = st.st_mtim;
153 if (utimensat(AT_FDCWD, s2, times, AT_SYMLINK_NOFOLLOW) < 0) {
154 weprintf("utimensat %s:", s2);
155 cp_status = 1;
156 }
157
158 /* owner and mode */
159 if (!S_ISLNK(st.st_mode)) {
160 if (chown(s2, st.st_uid, st.st_gid) < 0) {
161 weprintf("chown %s:", s2);
162 cp_status = 1;
163 st.st_mode &= ~(S_ISUID | S_ISGID);
164 }
165 if (chmod(s2, st.st_mode) < 0) {
166 weprintf("chmod %s:", s2);
167 cp_status = 1;
168 }
169 } else {
170 if (lchown(s2, st.st_uid, st.st_gid) < 0) {
171 weprintf("lchown %s:", s2);
172 cp_status = 1;
173 return 0;
174 }
175 }
176 }
177
178 return 0;
179}