78ed81a3 |
1 | /* copypass.c - cpio copy pass sub-function. |
2 | Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc. |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation; either version 2, or (at your option) |
7 | any later version. |
8 | |
9 | This program is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | GNU General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU General Public License |
15 | along with this program; if not, write to the Free Software |
16 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ |
17 | |
18 | #include <stdio.h> |
19 | #include <sys/types.h> |
20 | #include <sys/stat.h> |
21 | #include "filetypes.h" |
22 | #include "system.h" |
23 | #include "cpiohdr.h" |
24 | #include "dstring.h" |
25 | #include "extern.h" |
26 | |
27 | #ifndef HAVE_LCHOWN |
28 | #define lchown chown |
29 | #endif |
30 | |
31 | /* Copy files listed on the standard input into directory `directory_name'. |
32 | If `link_flag', link instead of copying. */ |
33 | |
34 | void |
35 | process_copy_pass () |
36 | { |
37 | dynamic_string input_name; /* Name of file from stdin. */ |
38 | dynamic_string output_name; /* Name of new file. */ |
39 | int dirname_len; /* Length of `directory_name'. */ |
40 | int res; /* Result of functions. */ |
41 | char *slash; /* For moving past slashes in input name. */ |
42 | struct utimbuf times; /* For resetting file times after copy. */ |
43 | struct stat in_file_stat; /* Stat record for input file. */ |
44 | struct stat out_file_stat; /* Stat record for output file. */ |
45 | int in_file_des; /* Input file descriptor. */ |
46 | int out_file_des; /* Output file descriptor. */ |
47 | int existing_dir; /* True if file is a dir & already exists. */ |
48 | #ifdef HPUX_CDF |
49 | int cdf_flag; |
50 | int cdf_char; |
51 | #endif |
52 | |
53 | /* Initialize the copy pass. */ |
54 | dirname_len = strlen (directory_name); |
55 | ds_init (&input_name, 128); |
56 | ds_init (&output_name, dirname_len + 2); |
57 | strcpy (output_name.ds_string, directory_name); |
58 | output_name.ds_string[dirname_len] = '/'; |
59 | output_is_seekable = TRUE; |
60 | /* Initialize this in case it has members we don't know to set. */ |
61 | bzero (×, sizeof (struct utimbuf)); |
62 | |
63 | /* Copy files with names read from stdin. */ |
64 | while (ds_fgetstr (stdin, &input_name, name_end) != NULL) |
65 | { |
66 | int link_res = -1; |
67 | |
68 | /* Check for blank line and ignore it if found. */ |
69 | if (input_name.ds_string[0] == '\0') |
70 | { |
71 | error (0, 0, "blank line ignored"); |
72 | continue; |
73 | } |
74 | |
75 | /* Check for current directory and ignore it if found. */ |
76 | if (input_name.ds_string[0] == '.' |
77 | && (input_name.ds_string[1] == '\0' |
78 | || (input_name.ds_string[1] == '/' |
79 | && input_name.ds_string[2] == '\0'))) |
80 | continue; |
81 | |
82 | if ((*xstat) (input_name.ds_string, &in_file_stat) < 0) |
83 | { |
84 | error (0, errno, "%s", input_name.ds_string); |
85 | continue; |
86 | } |
87 | |
88 | /* Make the name of the new file. */ |
89 | for (slash = input_name.ds_string; *slash == '/'; ++slash) |
90 | ; |
91 | #ifdef HPUX_CDF |
92 | /* For CDF's we add a 2nd `/' after all "hidden" directories. |
93 | This kind of a kludge, but it's what we do when creating |
94 | archives, and it's easier to do this than to separately |
95 | keep track of which directories in a path are "hidden". */ |
96 | slash = add_cdf_double_slashes (slash); |
97 | #endif |
98 | ds_resize (&output_name, dirname_len + strlen (slash) + 2); |
99 | strcpy (output_name.ds_string + dirname_len + 1, slash); |
100 | |
101 | existing_dir = FALSE; |
102 | if (lstat (output_name.ds_string, &out_file_stat) == 0) |
103 | { |
104 | if (S_ISDIR (out_file_stat.st_mode) |
105 | && S_ISDIR (in_file_stat.st_mode)) |
106 | { |
107 | /* If there is already a directory there that |
108 | we are trying to create, don't complain about it. */ |
109 | existing_dir = TRUE; |
110 | } |
111 | else if (!unconditional_flag |
112 | && in_file_stat.st_mtime <= out_file_stat.st_mtime) |
113 | { |
114 | error (0, 0, "%s not created: newer or same age version exists", |
115 | output_name.ds_string); |
116 | continue; /* Go to the next file. */ |
117 | } |
118 | else if (S_ISDIR (out_file_stat.st_mode) |
119 | ? rmdir (output_name.ds_string) |
120 | : unlink (output_name.ds_string)) |
121 | { |
122 | error (0, errno, "cannot remove current %s", |
123 | output_name.ds_string); |
124 | continue; /* Go to the next file. */ |
125 | } |
126 | } |
127 | |
128 | /* Do the real copy or link. */ |
129 | if (S_ISREG (in_file_stat.st_mode)) |
130 | { |
131 | #ifndef __MSDOS__ |
132 | /* Can the current file be linked to a another file? |
133 | Set link_name to the original file name. */ |
134 | if (link_flag) |
135 | /* User said to link it if possible. Try and link to |
136 | the original copy. If that fails we'll still try |
137 | and link to a copy we've already made. */ |
138 | link_res = link_to_name (output_name.ds_string, |
139 | input_name.ds_string); |
140 | if ( (link_res < 0) && (in_file_stat.st_nlink > 1) ) |
141 | link_res = link_to_maj_min_ino (output_name.ds_string, |
142 | major (in_file_stat.st_dev), |
143 | minor (in_file_stat.st_dev), |
144 | in_file_stat.st_ino); |
145 | #endif |
146 | |
147 | /* If the file was not linked, copy contents of file. */ |
148 | if (link_res < 0) |
149 | { |
150 | in_file_des = open (input_name.ds_string, |
151 | O_RDONLY | O_BINARY, 0); |
152 | if (in_file_des < 0) |
153 | { |
154 | error (0, errno, "%s", input_name.ds_string); |
155 | continue; |
156 | } |
157 | out_file_des = open (output_name.ds_string, |
158 | O_CREAT | O_WRONLY | O_BINARY, 0600); |
159 | if (out_file_des < 0 && create_dir_flag) |
160 | { |
161 | create_all_directories (output_name.ds_string); |
162 | out_file_des = open (output_name.ds_string, |
163 | O_CREAT | O_WRONLY | O_BINARY, 0600); |
164 | } |
165 | if (out_file_des < 0) |
166 | { |
167 | error (0, errno, "%s", output_name.ds_string); |
168 | close (in_file_des); |
169 | continue; |
170 | } |
171 | |
172 | copy_files (in_file_des, out_file_des, in_file_stat.st_size); |
173 | empty_output_buffer (out_file_des); |
174 | if (close (in_file_des) < 0) |
175 | error (0, errno, "%s", input_name.ds_string); |
176 | if (close (out_file_des) < 0) |
177 | error (0, errno, "%s", output_name.ds_string); |
178 | |
179 | /* Set the attributes of the new file. */ |
180 | if (!no_chown_flag) |
181 | if ((chown (output_name.ds_string, |
182 | set_owner_flag ? set_owner : in_file_stat.st_uid, |
183 | set_group_flag ? set_group : in_file_stat.st_gid) < 0) |
184 | && errno != EPERM) |
185 | error (0, errno, "%s", output_name.ds_string); |
186 | /* chown may have turned off some permissions we wanted. */ |
187 | if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0) |
188 | error (0, errno, "%s", output_name.ds_string); |
189 | if (reset_time_flag) |
190 | { |
191 | times.actime = in_file_stat.st_atime; |
192 | times.modtime = in_file_stat.st_mtime; |
193 | if (utime (input_name.ds_string, ×) < 0) |
194 | error (0, errno, "%s", input_name.ds_string); |
195 | if (utime (output_name.ds_string, ×) < 0) |
196 | error (0, errno, "%s", output_name.ds_string); |
197 | } |
198 | if (retain_time_flag) |
199 | { |
200 | times.actime = times.modtime = in_file_stat.st_mtime; |
201 | if (utime (output_name.ds_string, ×) < 0) |
202 | error (0, errno, "%s", output_name.ds_string); |
203 | } |
204 | } |
205 | } |
206 | else if (S_ISDIR (in_file_stat.st_mode)) |
207 | { |
208 | #ifdef HPUX_CDF |
209 | cdf_flag = 0; |
210 | #endif |
211 | if (!existing_dir) |
212 | { |
213 | #ifdef HPUX_CDF |
214 | /* If the directory name ends in a + and is SUID, |
215 | then it is a CDF. Strip the trailing + from the name |
216 | before creating it. */ |
217 | cdf_char = strlen (output_name.ds_string) - 1; |
218 | if ( (cdf_char > 0) && |
219 | (in_file_stat.st_mode & 04000) && |
220 | (output_name.ds_string [cdf_char] == '+') ) |
221 | { |
222 | output_name.ds_string [cdf_char] = '\0'; |
223 | cdf_flag = 1; |
224 | } |
225 | #endif |
226 | res = mkdir (output_name.ds_string, in_file_stat.st_mode); |
227 | |
228 | } |
229 | else |
230 | res = 0; |
231 | if (res < 0 && create_dir_flag) |
232 | { |
233 | create_all_directories (output_name.ds_string); |
234 | res = mkdir (output_name.ds_string, in_file_stat.st_mode); |
235 | } |
236 | if (res < 0) |
237 | { |
238 | error (0, errno, "%s", output_name.ds_string); |
239 | continue; |
240 | } |
241 | if (!no_chown_flag) |
242 | if ((chown (output_name.ds_string, |
243 | set_owner_flag ? set_owner : in_file_stat.st_uid, |
244 | set_group_flag ? set_group : in_file_stat.st_gid) < 0) |
245 | && errno != EPERM) |
246 | error (0, errno, "%s", output_name.ds_string); |
247 | /* chown may have turned off some permissions we wanted. */ |
248 | if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0) |
249 | error (0, errno, "%s", output_name.ds_string); |
250 | #ifdef HPUX_CDF |
251 | if (cdf_flag) |
252 | /* Once we "hide" the directory with the chmod(), |
253 | we have to refer to it using name+ isntead of name. */ |
254 | output_name.ds_string [cdf_char] = '+'; |
255 | #endif |
256 | if (retain_time_flag) |
257 | { |
258 | times.actime = times.modtime = in_file_stat.st_mtime; |
259 | if (utime (output_name.ds_string, ×) < 0) |
260 | error (0, errno, "%s", output_name.ds_string); |
261 | } |
262 | } |
263 | #ifndef __MSDOS__ |
264 | else if (S_ISCHR (in_file_stat.st_mode) || |
265 | S_ISBLK (in_file_stat.st_mode) || |
266 | #ifdef S_ISFIFO |
267 | S_ISFIFO (in_file_stat.st_mode) || |
268 | #endif |
269 | #ifdef S_ISSOCK |
270 | S_ISSOCK (in_file_stat.st_mode) || |
271 | #endif |
272 | 0) |
273 | { |
274 | /* Can the current file be linked to a another file? |
275 | Set link_name to the original file name. */ |
276 | if (link_flag) |
277 | /* User said to link it if possible. */ |
278 | link_res = link_to_name (output_name.ds_string, |
279 | input_name.ds_string); |
280 | if ( (link_res < 0) && (in_file_stat.st_nlink > 1) ) |
281 | link_res = link_to_maj_min_ino (output_name.ds_string, |
282 | major (in_file_stat.st_dev), |
283 | minor (in_file_stat.st_dev), |
284 | in_file_stat.st_ino); |
285 | |
286 | if (link_res < 0) |
287 | { |
288 | res = mknod (output_name.ds_string, in_file_stat.st_mode, |
289 | in_file_stat.st_rdev); |
290 | if (res < 0 && create_dir_flag) |
291 | { |
292 | create_all_directories (output_name.ds_string); |
293 | res = mknod (output_name.ds_string, in_file_stat.st_mode, |
294 | in_file_stat.st_rdev); |
295 | } |
296 | if (res < 0) |
297 | { |
298 | error (0, errno, "%s", output_name.ds_string); |
299 | continue; |
300 | } |
301 | if (!no_chown_flag) |
302 | if ((chown (output_name.ds_string, |
303 | set_owner_flag ? set_owner : in_file_stat.st_uid, |
304 | set_group_flag ? set_group : in_file_stat.st_gid) < 0) |
305 | && errno != EPERM) |
306 | error (0, errno, "%s", output_name.ds_string); |
307 | /* chown may have turned off some permissions we wanted. */ |
308 | if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0) |
309 | error (0, errno, "%s", output_name.ds_string); |
310 | if (retain_time_flag) |
311 | { |
312 | times.actime = times.modtime = in_file_stat.st_mtime; |
313 | if (utime (output_name.ds_string, ×) < 0) |
314 | error (0, errno, "%s", output_name.ds_string); |
315 | } |
316 | } |
317 | } |
318 | #endif |
319 | |
320 | #ifdef S_ISLNK |
321 | else if (S_ISLNK (in_file_stat.st_mode)) |
322 | { |
323 | char *link_name; |
324 | link_name = (char *) xmalloc ((unsigned int) in_file_stat.st_size + 1); |
325 | |
326 | if (readlink (input_name.ds_string, link_name, |
327 | in_file_stat.st_size) < 0) |
328 | { |
329 | error (0, errno, "%s", input_name.ds_string); |
330 | free (link_name); |
331 | continue; |
332 | } |
333 | link_name[in_file_stat.st_size] = '\0'; |
334 | |
335 | res = UMASKED_SYMLINK (link_name, output_name.ds_string, |
336 | in_file_stat.st_mode); |
337 | if (res < 0 && create_dir_flag) |
338 | { |
339 | create_all_directories (output_name.ds_string); |
340 | res = UMASKED_SYMLINK (link_name, output_name.ds_string, |
341 | in_file_stat.st_mode); |
342 | } |
343 | if (res < 0) |
344 | { |
345 | error (0, errno, "%s", output_name.ds_string); |
346 | free (link_name); |
347 | continue; |
348 | } |
349 | |
350 | /* Set the attributes of the new link. */ |
351 | if (!no_chown_flag) |
352 | if ((lchown (output_name.ds_string, |
353 | set_owner_flag ? set_owner : in_file_stat.st_uid, |
354 | set_group_flag ? set_group : in_file_stat.st_gid) < 0) |
355 | && errno != EPERM) |
356 | error (0, errno, "%s", output_name.ds_string); |
357 | free (link_name); |
358 | } |
359 | #endif |
360 | else |
361 | { |
362 | error (0, 0, "%s: unknown file type", input_name.ds_string); |
363 | } |
364 | |
365 | if (verbose_flag) |
366 | fprintf (stderr, "%s\n", output_name.ds_string); |
367 | if (dot_flag) |
368 | fputc ('.', stderr); |
369 | } |
370 | |
371 | if (dot_flag) |
372 | fputc ('\n', stderr); |
373 | res = (output_bytes + io_block_size - 1) / io_block_size; |
374 | if (res == 1) |
375 | fprintf (stderr, "1 block\n"); |
376 | else |
377 | fprintf (stderr, "%d blocks\n", res); |
378 | } |
379 | \f |
380 | /* Try and create a hard link from FILE_NAME to another file |
381 | with the given major/minor device number and inode. If no other |
382 | file with the same major/minor/inode numbers is known, add this file |
383 | to the list of known files and associated major/minor/inode numbers |
384 | and return -1. If another file with the same major/minor/inode |
385 | numbers is found, try and create another link to it using |
386 | link_to_name, and return 0 for success and -1 for failure. */ |
387 | |
388 | int |
389 | link_to_maj_min_ino (file_name, st_dev_maj, st_dev_min, st_ino) |
390 | char *file_name; |
391 | int st_dev_maj; |
392 | int st_dev_min; |
393 | int st_ino; |
394 | { |
395 | int link_res; |
396 | char *link_name; |
397 | link_res = -1; |
398 | #ifndef __MSDOS__ |
399 | /* Is the file a link to a previously copied file? */ |
400 | link_name = find_inode_file (st_ino, |
401 | st_dev_maj, |
402 | st_dev_min); |
403 | if (link_name == NULL) |
404 | add_inode (st_ino, file_name, |
405 | st_dev_maj, |
406 | st_dev_min); |
407 | else |
408 | link_res = link_to_name (file_name, link_name); |
409 | #endif |
410 | return link_res; |
411 | } |
412 | \f |
413 | /* Try and create a hard link from LINK_NAME to LINK_TARGET. If |
414 | `create_dir_flag' is set, any non-existent (parent) directories |
415 | needed by LINK_NAME will be created. If the link is successfully |
416 | created and `verbose_flag' is set, print "LINK_TARGET linked to LINK_NAME\n". |
417 | If the link can not be created and `link_flag' is set, print |
418 | "cannot link LINK_TARGET to LINK_NAME\n". Return 0 if the link |
419 | is created, -1 otherwise. */ |
420 | |
421 | int |
422 | link_to_name (link_name, link_target) |
423 | char *link_name; |
424 | char *link_target; |
425 | { |
426 | int res; |
427 | #ifdef __MSDOS__ |
428 | res = -1; |
429 | #else /* not __MSDOS__ */ |
430 | res = link (link_target, link_name); |
431 | if (res < 0 && create_dir_flag) |
432 | { |
433 | create_all_directories (link_name); |
434 | res = link (link_target, link_name); |
435 | } |
436 | if (res == 0) |
437 | { |
438 | if (verbose_flag) |
439 | error (0, 0, "%s linked to %s", |
440 | link_target, link_name); |
441 | } |
442 | else if (link_flag) |
443 | { |
444 | error (0, errno, "cannot link %s to %s", |
445 | link_target, link_name); |
446 | } |
447 | #endif /* not __MSDOS__ */ |
448 | return res; |
449 | } |