comparison toys/posix/cp.c @ 790:6aa6efdd5883

Make "sudo cp -rp /dev/null blah" work. Still not happy with it, fchmodat(AT_SYMLINK_NOFOLLOW) doesn't work (there's a glibc bug open for this. It's really a missing kernel syscall, but glibc fails without ever making any syscall if you feed it that flag, which isn't helpful).
author Rob Landley <rob@landley.net>
date Wed, 16 Jan 2013 06:57:44 -0600
parents 5bc258a4c750
children f8f5ddb6b69a
comparison
equal deleted inserted replaced
789:9a6b08e7fe94 790:6aa6efdd5883
95 } 95 }
96 96
97 // Loop for -f retry after unlink 97 // Loop for -f retry after unlink
98 do { 98 do {
99 99
100 // directory, hardlink, symlink, mknod (char, block, fifo, socket), file
101
100 // Copy directory 102 // Copy directory
101 103
102 if (S_ISDIR(try->st.st_mode)) { 104 if (S_ISDIR(try->st.st_mode)) {
103 struct stat st2; 105 struct stat st2;
104 106
114 // possible that -p can be made to chown a directory other than the one 116 // possible that -p can be made to chown a directory other than the one
115 // we created. The closest we can do to closing this is make sure 117 // we created. The closest we can do to closing this is make sure
116 // that what we open _is_ a directory rather than something else. 118 // that what we open _is_ a directory rather than something else.
117 119
118 if (!mkdirat(cfd, catch, try->st.st_mode | 0200) || errno == EEXIST) 120 if (!mkdirat(cfd, catch, try->st.st_mode | 0200) || errno == EEXIST)
119 if (-1 != (try->extra = openat(cfd, catch, 0))) 121 if (-1 != (try->extra = openat(cfd, catch, O_NOFOLLOW)))
120 if (!fstat(try->extra, &st2)) 122 if (!fstat(try->extra, &st2))
121 if (S_ISDIR(st2.st_mode)) return DIRTREE_COMEAGAIN; 123 if (S_ISDIR(st2.st_mode)) return DIRTREE_COMEAGAIN;
122 124
123 // Hardlink 125 // Hardlink
124 126
133 if (S_ISLNK(try->st.st_mode)) { 135 if (S_ISLNK(try->st.st_mode)) {
134 int i = readlinkat(tfd, try->name, toybuf, sizeof(toybuf)); 136 int i = readlinkat(tfd, try->name, toybuf, sizeof(toybuf));
135 if (i < 1 || i >= sizeof(toybuf)) break; 137 if (i < 1 || i >= sizeof(toybuf)) break;
136 else if (!symlinkat(toybuf, cfd, catch)) err = 0; 138 else if (!symlinkat(toybuf, cfd, catch)) err = 0;
137 // block, char, fifo, socket 139 // block, char, fifo, socket
138 } else if (!mknodat(cfd, catch, try->st.st_mode, try->st.st_dev)) 140 } else if (!mknodat(cfd, catch, try->st.st_mode, try->st.st_rdev)) {
139 if (!(flags & (FLAG_a|FLAG_p)) 141 err = 0;
140 || -1 != (fdout = openat(cfd, catch, O_RDONLY))) err = 0; 142 if (flags & (FLAG_a|FLAG_p)) fdout = AT_FDCWD;
143 }
141 144
142 // Copy contents of file. 145 // Copy contents of file.
143 } else { 146 } else {
144 int fdin; 147 int fdin;
145 148
163 if (flags & (FLAG_a|FLAG_p)) { 166 if (flags & (FLAG_a|FLAG_p)) {
164 struct timespec times[2]; 167 struct timespec times[2];
165 168
166 // Inability to set these isn't fatal, some require root access. 169 // Inability to set these isn't fatal, some require root access.
167 170
168 fchown(fdout, try->st.st_uid, try->st.st_gid);
169 times[0] = try->st.st_atim; 171 times[0] = try->st.st_atim;
170 times[1] = try->st.st_mtim; 172 times[1] = try->st.st_mtim;
171 futimens(fdout, times); 173
172 fchmod(fdout, try->st.st_mode); 174 // If we can't get a filehandle to the actual object, use racy functions
173 } 175 if (fdout == AT_FDCWD) {
174 176 if (fchownat(cfd, catch, try->st.st_uid, try->st.st_gid,
175 xclose(fdout); 177 AT_SYMLINK_NOFOLLOW)
178 || utimensat(cfd, catch, times, AT_SYMLINK_NOFOLLOW)
179 || fchmodat(cfd, catch, try->st.st_mode&07777, 0))
180 err = "%s";
181 } else {
182 if (fchown(fdout, try->st.st_uid, try->st.st_gid)
183 || futimens(fdout, times) || fchmod(fdout, try->st.st_mode&07777))
184 err = "%s";
185 }
186 }
187
188 if (fdout != AT_FDCWD) xclose(fdout);
176 } 189 }
177 190
178 if (err) perror_msg(err, catch); 191 if (err) perror_msg(err, catch);
179 return 0; 192 return 0;
180 } 193 }