cp.c - sbase - suckless unix tools
(HTM) git clone git://git.suckless.org/sbase
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
cp.c (3791B)
---
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
17 int cp_aflag = 0;
18 int cp_fflag = 0;
19 int cp_iflag = 0;
20 int cp_pflag = 0;
21 int cp_rflag = 0;
22 int cp_vflag = 0;
23 int cp_status = 0;
24 int cp_follow;
25
26 int
27 cp(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 (cp_aflag && (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) ||
104 S_ISSOCK(st.st_mode) || S_ISFIFO(st.st_mode))) {
105 if (cp_fflag && unlink(s2) < 0 && errno != ENOENT) {
106 weprintf("unlink %s:", s2);
107 cp_status = 1;
108 return 0;
109 } else if (mknod(s2, st.st_mode, st.st_rdev) < 0) {
110 weprintf("mknod %s:", s2);
111 cp_status = 1;
112 return 0;
113 }
114 } else {
115 if ((f1 = open(s1, O_RDONLY)) < 0) {
116 weprintf("open %s:", s1);
117 cp_status = 1;
118 return 0;
119 }
120 if ((f2 = creat(s2, st.st_mode)) < 0 && cp_fflag) {
121 if (unlink(s2) < 0 && errno != ENOENT) {
122 weprintf("unlink %s:", s2);
123 cp_status = 1;
124 close(f1);
125 return 0;
126 }
127 f2 = creat(s2, st.st_mode);
128 }
129 if (f2 < 0) {
130 weprintf("creat %s:", s2);
131 cp_status = 1;
132 close(f1);
133 return 0;
134 }
135 if (concat(f1, s1, f2, s2) < 0) {
136 cp_status = 1;
137 close(f1);
138 close(f2);
139 return 0;
140 }
141
142 close(f1);
143 close(f2);
144 }
145
146 if (cp_aflag || cp_pflag) {
147 /* atime and mtime */
148 times[0] = st.st_atim;
149 times[1] = st.st_mtim;
150 if (utimensat(AT_FDCWD, s2, times, AT_SYMLINK_NOFOLLOW) < 0) {
151 weprintf("utimensat %s:", s2);
152 cp_status = 1;
153 }
154
155 /* owner and mode */
156 if (!S_ISLNK(st.st_mode)) {
157 if (chown(s2, st.st_uid, st.st_gid) < 0) {
158 weprintf("chown %s:", s2);
159 cp_status = 1;
160 st.st_mode &= ~(S_ISUID | S_ISGID);
161 }
162 if (chmod(s2, st.st_mode) < 0) {
163 weprintf("chmod %s:", s2);
164 cp_status = 1;
165 }
166 } else {
167 if (lchown(s2, st.st_uid, st.st_gid) < 0) {
168 weprintf("lchown %s:", s2);
169 cp_status = 1;
170 return 0;
171 }
172 }
173 }
174
175 return 0;
176 }